• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

TypeScript从入门到项目实战(进阶篇)

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

 

 

 

内置对象的使用

JavaScript中有许多内置对象,可以直接在JavaScript程序中使用,同样的,TypeScript也延续了这些内置对象

全局的对象( global objects )或称标准内置对象,不要和 "全局对象(global object)混淆。这里说的全局的对象是说在全局作用域里的对象

ECMAScript内置对象

ECMAScript标准提供的内置对象有:
ObjectErrorNumberDateBooleanArray等:

const boolean: Boolean = new Boolean(true)
const date: Date = new Date()
const n: Number = new Number(20)

更多内置对象,请看JavaScript 标准内置对象

DOM、BOM内置对象

DOM和BOM常用的有:

Document、HTMLElement、Event、NodeList、Node、等,在DOM操作中很有用

let body: HTMLElement = document.body
let allDiv: NodeList = document.querySelectorAll(\'div\')
document.addEventListener(\'click\', function(e: MouseEvent) {
  // Do something
})

更多请看文档对象模型 (DOM) 

函数

函数中的this

在JavaScript中,this使用可能有时候并不是如自己所想的那样

但是好消息是,TypeScript中会提示你是否正确的使用了this。

如下面的例子中,SVGElement是window的一个方法,所以编辑器会报错:

document.querySelector(\'body\').addEventListener(\'click\',function (event:MouseEvent) {
  this.nodeName
  this.SVGElement//Property \'SVGElement\' does not exist on type \'HTMLBodyElement\'.
})

箭头函数与this

首先来个例子

const awardsInfo = {
  name: \'陈灵十\',
  age: \'10\',
  prize: \'三等奖\',
  takePart() {
    return function(){
      console.log(`姓名:${this.name},年龄:${this.age},参加全国青少年科技创新大赛,获得${this.prize}`)
    }
  }
}

const awards=awardsInfo.takePart()
//期待:姓名:陈灵十,年龄:10,参加全国青少年科技创新大赛,获得三等奖
//结果:姓名:undefined,年龄:undefined,参加全国青少年科技创新大赛,获得undefined
awards()

可以看到,这里预期的结果和实际结果并不一致。原因其实也很简单,因为awards运行在全局作用域下,而调用时,awards中的this为window,而window并没有nameageprize这些属性。

知道原因我们就可以很好的解决这个问题,利用ES6的箭头函数:

takePart() {
    return () => {
      console.log(`姓名:${this.name},年龄:${this.age},参加全国青少年科技创新大赛,获得${this.prize}`)
    }
  }

箭头函数有几个使用注意点。

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

更多关于ES6箭头函数请看传送门:箭头函数

this参数

现在有一个超人岑一诺,定义了两个接口:超能力(SuperPowers)、超人(SuperHuman),代码如下:

interface SuperPowers {
  name: string,
  describe: string;
  toString: () => string;
}

interface SuperHuman {
  name: string;
  sex: \'男\' | \'女\';
  superPowers: Array<SuperPowers>;
  introduce: () => Function
}

const CenYinuo: SuperHuman = {
  name: \'岑一诺\',
  sex: \'女\',
  superPowers: [
    {
      name: \'日均作词牌三百首\',
      describe: \'使用自己开发的文学机器人,在十分钟内快速作词,十分钟之后做出的词,李清照见了羞愧难当,\' +
        \'辛弃疾见了直接弃文,王安石见了直接拜师,苏轼见了直接退出唐宋八大家的群\',
      toString() {
        return `${this.name}:${this.describe}`
      }
    },
    {
      name: \'日均作诗两千首\',
      describe: \'使用自己开发的文学机器人,半小时内快速作诗,半小时后,李白见了开头让出诗仙称号,杜甫未闻其诗听后自惭形秽\',
      toString() {
        return `${this.name}:${this.describe}`
      }
    }
  ],
  introduce(): Function {
    return () => {
      const power1 = this.superPowers[0]
      const power2 = this.superPowers[1]
      console.log(`姓名:${this.name}\n性别${this.sex}\n超能力:\n\t1.${power1.toString()},\n\t2.${power2.toString()}`)
    }
  }
}
const introduce = CenYinuo.introduce()
introduce()
// 姓名:岑一诺
// 性别女
// 超能力:
// 	……

虽然代码能够正常运行(这段代码本身没有问题),但是有一个问题:TypeScript无法正确推断出其中数组的类型

这个问题并无大碍,但是开发起来没有那么舒服了:假如superPowers里面有很多复杂的类型,这时候开发起来就会很难受(不得不说TS的编辑器提示是真的好用)。既然发现了问题,该如何解决呢,这时候可能就有人会说了:

那就解决提出问题的人!

老子反手就是一个TM四连:

其实要解决这个问题也很简单,我们只要告诉TypeScript这个函数的this是什么类型就可以了,没错,就是this参数

当你将一个函数传递到某个库函数里稍后会被调用时。 因为当回调被调用的时候,它们会被当成一个普通函数调用, this将为undefined。 稍做改动,你就可以通过 this参数来避免错误。

 

首先,库函数的作者要指定 this的类型;

然后函数要被调用,这样TypeScript才能检测到this的类型。

我们只需要对接口SuperHuman进行一点点改动

interface SuperHuman {
  name: string;
  sex: \'男\' | \'女\';
  superPowers: Array<SuperPowers>;
  introduce: (this:SuperHuman) => Function
}

我们可以看到这个时候TypeScript已经检测到了数组中解析出来的类型:

 

函数重载

什么是函数重载?摘一段维基百科的原话:

函数重载(英语:function overloading),是AdaC++C#、D和Java编程语言中具有的一项特性,这项特性允许创建数项名称相同但输入输出类型或个数不同的子程序,它可以简单地称为一个单独功能可以执行多项任务的能力。

在TypeScript中,允许我们为函数定义不同参数返回不同类型,例如下面的例子:

function reverse(val: number): number
function reverse(val: string): string
function reverse(val: Array<any>): Array<any>
function reverse(val: number | string | Array<any>): number | string | Array<any> {
  if (typeof val === \'number\') {
    return +val.toString().split(\'\').reverse().join(\'\')
  } else if (typeof val === \'string\') {
    return val.toString().split(\'\').reverse().join(\'\')
  } else {
    return val.reverse()
  }
}

console.log(reverse(2020))
console.log(reverse(\'hello word!\'))
console.log(reverse([1, 2, 3, 4]))
// 输出结果:
// 202
// !drow olleh
// [ 4, 3, 2, 1 ]

是不是感觉很神奇,TypeScript类型检查通过,让我们分别看看上面三个函数的类型检测为什么:

 

reverse(2020)检测类型为function reverse(val: number): number

reverse(\'hello word!\')检测类型为function reverse(val: string): string

reverse([1, 2, 3, 4])检测类型为function reverse(val: Array<any>): Array<any>

这个例子已经初见其好处,对于一些复杂逻辑的函数,重载能够发挥更大的作用

(英语:class)在面向对象编程中是一种面向对象计算机编程语言的构造,是创建对象的蓝图,描述了所创建的对象共同的属性方法

类的更严格的定义是由某种特定的元数据所组成的内聚的包。它描述了一些对象的行为规则,而这些对象就被称为该类的实例。类有接口和结构。接口描述了如何通过方法与类及其实例互操作,而结构描述了一个实例中数据如何划分为多个属性。类是与某个层[注 1]的对象的最具体的类型。类还可以有运行时表示形式(元对象),它为操作与类相关的元数据提供了运行时支持。

虽然JavaScript中有类的概念,但是很多前端同学并不是很熟悉,一来是不是经常使用,二来,es6之前的类,都不像是类,而是像函数,因为es6之前都是用构造函数实现类的:

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return \'(\' + this.x + \', \' + this.y + \')\';
};

var p = new Point(1, 2);

和传统的面向对象语言(Java、C++、C#等)相比,JavaScript的类的实现就显得很“怪异”,可能他们的内心OS是这样的:

为了让JavaScript更加有排面,ES6之后对传统的类进行类封装,新增Class(类),其本质是传统类的语法糖,本文假设你已经知道了ES6+的Class(如果你还不知道,请进入传送门:Class的基本语法),TypeScript作为JavaScript的扩展,在支持ES6+的Class的同时,还对这种语法进行了扩展,让Class更加灵活、更加的“面向对象”,所以我们不详说ES中的class,我们只说TypeScript对class的扩展。

 

语法(英语:Syntactic sugar)是由英国计算机科学家彼得·兰丁发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。语法让程序更加简洁,有更高的可读性。 

 

修饰符

TypeScript为Class扩展了三个修饰符:

  • public:修饰的属性或者方法是公有地,可以在任何地方访问到修饰的属性或者方法,这也是属性或者方法的默认值
  • private:表示属性或者方法是私有的,不能够在类之外访问,如果在外部访问会报错(但是能够通过编译)
  • protected:让属性和方法受保护,能够在类内部操作,但是只能在子类中被访问。

这三个修饰符在传统的面向对象语言中非常常见,如果你学过Java、C++之类的语言,会感到很亲切,但是如果你直接出国JavaScript,可能需要一点时间去接受它。

class Animal {
  private className = \'动物类\'
  protected des = \'这是动物类\'
  public species: string

  constructor(species: string) {
    this.species = species
  }
}

class Dog extends Animal {
  name: string

  constructor(varieties: string, name: string) {
    super(\'狗\')
    console.log(this.species)
    console.log(this.des)
    this.name = name
  }
}

const dog = new Dog(\'中华田园犬\', \'dog\')
const animal=new Animal(\'马\')
console.log(animal.className)//Property \'className\' is private and only accessible within class \'Animal\'.
console.log(animal.des)//Property \'des\' is protected and only accessible within class \'Animal\' and its subclasses.
dog.des//Property \'des\' is protected and only accessible within class \'Animal\' and its subclasses.

注意:

当使用private修饰构造函数时,该类不允许被初始化或者被继承(这时候编译还是能够通过,只是TypeScript会报错):

class Animal {
  private constructor() {
  }
}

//Cannot extend a class \'Animal\'. Class constructor is marked as private.
class Dog extends Animal {
  constructor() {
    super()
  }
}

const dog = new Dog()
const animal = new Animal()//Constructor of class \'Animal\' is private and only accessible within the class declaration.

当使用protected修饰构造函数时,只能够被继承:

class Animal {
  protected constructor() {
  }
}

class Dog extends Animal {
  constructor() {
    super()
  }
}

const dog = new Dog()
const animal = new Animal()//Constructor of class \'Animal\' is private and only accessible within the class declaration.

参数属性

静态属性(public

使用public修饰参数时,表示定义属性并为其赋值,能够使代码更加简洁:

class Calendar extends Date {
  constructor(public year: number, public month: number) {
    super()
  }
}

const c: Calendar = new Calendar(2020, 7)
console.log(c.year)
console.log(c.month)

上面代码等同于下面的代码:

class Calendar extends Date {
  year:number
  month:number
  constructor(year:number,month:number) {
    super()
    this.year=year
    this.month=month
  }
}

const c: Calendar = new Calendar(2020,7)
console.log(c.year)
console.log(c.month)

只读属性(readonly

当你想某个值除了初始化,其他任何时候都不能赋值时,可以使用readonly修饰符修饰,当与其他修饰符同时出现时,其位置应该在改修饰符后面:

class Calendar extends Date {
  constructor(public readonly year: number) {
    super()
  }
}

const c: Calendar = new Calendar(2020)
c.year = 2019//Attempt to assign to const or readonly variable 

抽象类

传统面向对象编程语言中抽象类也是一个常见概念,下面是百度百科里面关于抽象类的介绍:

象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。

通常在编程语句中用 abstract 修饰的类是抽象类。在C++中,含有纯虚拟函数的类称为抽象类,它不能生成对象;在java中,含有抽象方法的类称为抽象类,同样不能生成对象。

在TypeScript中,抽象类不能够被实例化,只能够被实现或者继承,用abstract修饰;除此之外,属性和方法也能够被修饰:

abstract class MobileDevices {
  abstract monitor: string
  abstract CPU: string
  abstract GPU: string
  abstract RAM: string

  abstract powerOn()
}

class Phone extends MobileDevices {//

  constructor(
    public readonly CPU,
    public readonly GPU,
    public readonly RAM,
    public readonly monitor,
  ) {
    super()
  }

  powerOn() {
    console.log(\'按下开机键,缓缓响起开机音乐。两分钟之后。。。开机成功\')
  }

}

const md = new MobileDevices()//Cannot create an instance of an abstract class.
const nokiaElderlyPhone = new Phone(\'ARM11\', \'无\', \'512kb\', \'2寸大显示屏\')
nokiaElderlyPhone.powerOn()

或者phone类实现MobileDevices:

class Phone implements MobileDevices {

  constructor(
    public readonly CPU,
    public readonly GPU,
    public readonly RAM,
    public readonly monitor,
  ) {
  }

  powerOn() {
    console.log(\'按下开机键,缓缓响起开机音乐。两分钟之后。。。开机成功\')
  }

}

类作为类型

类定义除了能够让子类继承、实现,被实例化之外,还能够当做类型使用,其中用和接口使用方式类似:

const md: MobileDevices = {
  CPU: \'\',
  GPU: \'\',
  RAM: \'\',
  monitor: \'\',
  powerOn() {
  }

}
//下面的写法等同于上面的写法
const nokiaElderlyPhone: Phone = {
  CPU: \'\',
  GPU: \'\',
  RAM: \'\',
  monitor: \'\',
  powerOn(): any {
  }
}

类与接口

上面我们讲了Class的基本用法,下面我们看看接口与类之间的那些特殊用法。

接口继承

前面讲到,接口能够继承接口,但是你绝对想不到,接口还能继承类(继承了JavaScript的灵活性)

class Glass {
  capacity: string
  shape: string
  size: string

  load(food: string) {
    console.log(`装入${food}`)
  }
}

interface MugGlass extends Glass {
  CupHandleShape: string
}

const mugGlass: MugGlass = {
  CupHandleShape: \'\',
  capacity: \'\',
  shape: \'\',
  size: \'\',
  load(food: string): void {
  }
}

实现接口

既然类能实现类,那类能不能实现接口呢?答案是可以的,并且同时可以实现多个接口

interface GlassInterface {
  capacity: string
  shape: string
  size: string
  load: (food: string) => void
}

interface Structure {
  bodyShape: string;
  lidShape: string;
}

class MugGlass implements GlassInterface, Structure {
  bodyShape: string
  lidShape: string
  capacity: string
  shape: string
  size: string
  CupHandleShape: string

  load(food: string): void {
  }
}

const mugGlass: MugGlass = {
  bodyShape: \'\',
  lidShape: \'\',
  CupHandleShape: \'\',
  capacity: \'\',
  shape: \'\',
  size: \'\',
  load(food: string): void {
  }
}

泛型

介绍

泛型是什么?下面这段话摘自维基百科的泛型

泛型程序设计(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。AdaDelphiEiffelJavaC#F#Swift 和 Visual Basic .NET 称之为泛型(generics);MLScala 和 Haskell 称之为参数多态(parametric polymorphism);C++ 和 D称之为模板。具有广泛影响的1994年版的《Design Patterns》一书称之为参数化类型(parameterized type)。

 

TypeScript虽然是强类型约束的,但是同时又继承了JavaScript的灵活性,any和本章中的泛型就是其灵活性的体现。我们通过一个简单的例子来认识下泛型:

function createArray<T>(length: number, defaults: any): Array<T> {
  const arr: Array<T> = []
  for (let i = 0; i < length; i++) {
    arr.push(defaults)
  }
  return arr
}

const numArr = createArray<number>(20, 0)

上面代码中,我们通过调用方法时指定Tnumber类型(T只是一个类型变量,可以用其他字母代替,推荐使用单个大写字母定义),可以在函数作用域内使用该类型T,而TypeScript也会根据调用时指定的额泛型类型制动推断numArr的类型:


也可以定义多个泛型类型,如下面的例子:

function swap<T, U>(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]];
}

swap([7, \'seven\']); // [\'seven\', 7]

约束泛型类型

泛型尽管很灵活,但是由于没有制定具体类型,TypeScript无法自动推断类型,这时候我们就可以指定泛型有哪些属性:

async function god2BaiduAsync<T extends number>(gdLon: T, gdLat: T, wait = 0): Promise<[T, T]> {
  return new Promise(resolve => {
    setTimeout(() => {
      let bdLatLon: [T, T] = [gdLon, gdLat]
      let PI = 3.14159265358979324 * 3000.0 / 180.0
      let x = gdLon
      let y = gdLat
      let z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * PI)
      let theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * PI)
      bdLatLon[0] = <T>(z * Math.cos(theta) + 0.0065)//泛型类型断言,这里面如果不断言为T将会报错
      bdLatLon[1] = <T>(z * Math.sin(theta) + 0.006)
      resolve(bdLatLon)
    }, wait)
  })
}

//Argument of type \'"113.97471"\' is not assignable to parameter of type \'number\'.
const baiduPosition = god2BaiduAsync<number>(\'113.97471\', \'22.660848\')

const baiduPosition2 = god2BaiduAsync<number>(113.97471, 22.660848)

上面将高德地图经纬度转换为百度地图经纬度将泛型定义为number,调用时,如果不是number类型将会报错,同时对于有些情况下需要用泛型进行类型断言(类型断言在后面会讲到)。

泛型接口

之前我们写过一个createArray函数,如果我们用接口+泛型来定义其形状呢:

interface CreateArray {
  <T>(length: number, defaults: any): Array<T>
}

const createArray: CreateArray = <T>(length: number, defaults: any): Array<T> => {
  const arr: Array<T> = []
  for (let i = 0; i < length; i++) {
    arr.push(defaults)
  }
  return arr
}

const numArr = createArray<number>(20, 0)

我们可以对CreateArray做一点点优化,把泛型提升到接口名上:

interface CreateArray<T> {
  (length: number, defaults: T): Array<T>
}

const createArray: CreateArray<any> = <T>(length: number, defaults: any): Array<T> => {
  const arr: Array<T> = []
  for (let i = 0; i < length; i++) {
    arr.push(defaults)
  }
  return arr
}

const numArr = createArray(20, 0)

上一篇介绍数组的时候我们介绍过定义数组是使用泛型方式定义一个数组,现在我们简单地手动实现一个泛型数组:

interface PseudoArray<T> {
  [index: number]: T
}

let numArr: PseudoArray<number> = [1, 3]
let strNum: PseudoArray<string> = new Array(\'1\', \'2\', \'3\')
let bolArr:PseudoArray<boolean>=[1,3]//Type \'number\' is not assignable to type \'boolean\'.

泛型类

泛型除了可以用到接口中,还可以用到类中,让我们用class实现上面的泛型数组

class PseudoArray<T> {
  [index: number]: T

  constructor(...args) {
    return args
  }
}

let numArr: PseudoArray<number> = [1, 3]//[ 1, 3 ]
let strArr: PseudoArray<string> = new PseudoArray<string>(\'1\', \'2\', \'3\')//[ \'1\', \'2\', \'3\' ]
let bolArr: PseudoArray<boolean> = new PseudoArray<string>(\'1\', \'2\', \'3\')//Type \'PseudoArray<string>\' is not assignable to type \'PseudoArray<boolean>\'.   Type \'string\' is not assignable to type \'boolean\'.

泛型参数的默认值

我们可以为泛型设定默认类型:

function createArray<T = number>(length: number, defaults: any): Array<T> {
  const arr: Array<T> = []
  for (let i = 0; i < length; i++) {
    arr.push(defaults)
  }
  return arr
}

类型推论与类型兼容

类型推论

什么是类型推论?

TypeScript类型推论是怎么回事呢?TypeScript相信大家都很熟悉,但是TypeScript类型推论是怎么回事呢,下面就让小编带大家一起了解吧。

TypeScript类型推论,其实就是TypeScript内部实现了类型推论,大家可能会很惊讶TypeScript怎么会类型推论呢?但事实就是这样,小编也感到非常惊讶。

这就是关于TypeScript类型推论的事情了,大家有什么想法呢,欢迎在评论区告诉小编一起讨论哦!

好了,不皮了,放下你手中的啤酒瓶、板砖、狼牙棒,让我来好好说道说道


在TypeScript中,如果没定义类型则会自动根据所赋值内容自动推断类型,例如:

let num=1
num=\'1\'//Type \'"1"\' is not assignable to type \'number\'.

对于没有赋值的变量,则会自动推断为any类型:

let num
num = 1
num = \'1\'
num = false
num = true

最佳通用类型

当有多种类型的时候,TypeScript会使用这些类型来推断出一个最合适的类型:

let arr = [1, 2, true, \'3\']

如果你的编辑器对TypeScript支持比较友好的话,鼠标悬停在类型上面就会提示其类型推论的结果:

可以清晰地看到,TypeScript会推断为一个包含所有类型的联合类型


由于是选用所有类型作为其候选类型,所以当使用继承自相同父类型的子类型时,可能会出现偏差:

interface Man extends Human {
  
}
interface Woman extends Human {
  
}

const epson: Man = {
}

const marry: Woman = {
}
const peoples=[epson,marry]

这里类型推断为Array<Man>:

这时候我们可以手动指定其父类型作为其最合适的类型:

const peoples:Array<Human>=[epson,marry]

类型兼容

TypeScript的类型兼容性是基于结构子类型的,且不要求明确声明。

 

结构类型是一种只使用其成员来描述类型的方式,与名义类型形成对比。

名义类型:数据类型的兼容性或等价性是通过明确的声明和/或类型的名称来决定,名义数据类型语言(c#、java)

对象间的兼容

对象间的兼容比较简单,假如有两个对象:a、b,a要兼容b对象,则b至少需要与a相同的属性(相同的名称和类型,可比a多额外属性),例如:动物能够兼容人类(动物中包含了人类),但是人类不能兼容动物(不能说人类包含了动物)。

interface Computer {
  GPU: string;
  CPU: string;
  RAM: string;
}

interface Phone {
  GPU: string;
  CPU: string;
  RAM: string;
  screen: string;
}

let myComputer:Computer={
  CPU: \'线程撕裂者\',
  GPU: \'RTX2080Ti\',
  RAM: \'1T\'
}

let myPhone:Phone={
  CPU: \'麒麟1000\',
  GPU: \'无\',
  RAM: \'16G\',
  screen: \'三星\'
}

myComputer=myPhone

函数间的兼容

函数的兼容与对象有所不同,例如:x能够赋值给y,则x参数类型并须按顺序出现在y的参数列表里:

let x = (n: number) => n
let y = (a: number, b: string) => a
y = x
x=y//Type \'(a: number, b: string) => number\' is not assignable to type \'(n: number) => number\'.

这里y有两个参数,而x只有一个参数,TypeScript允许忽略其余参数,沿用了JavaScript的一贯做法,如:Array.filter(element:any,index?:number,array?:Array<any>),其中只有element是必须的,其余参数都可忽略

 

而对于返回值,其遵循对象兼容规则,例如:y的返回值需要兼容x的返回值,则x返回值至少需要包含y的返回值的属性:

let x: () => { n: number, s: string } = () => ({n: 1, s: \'\'})
let y: () => { n: number } = () => ({n: 1})
y = x
x = y// Type \'() => { n: number; }\' is not assignable to type \'() => { n: number; s: string; }\'.   Property \'s\' is missing in type \'{ n: number; }\' but required in type \'{ n: number; s: string; }\'.

枚举

不同枚举间即使值相同,也不能够兼容;但是枚举类型和数字类型能够相互兼容:

enum Duirection {
  right,
  left,
  bottom,
  top
}

enum Color {
  red,
  green,
  blue,
  none
}

enum MobileDevices {
  phone = \'phone\',
  computer = \'computer\',
  watch = \'watch\'
}

let d = Duirection.top
d = 1
d = Color.red//Type \'Color.red\' is not assignable to type \'Duirection\'.

let md = MobileDevices.phone
md = \'computer\'//Type \'"computer"\' is not assignable to type \'MobileDevices\'.

const num: number = Duirection.top

类与字面量的兼容差不多,有一点不同:类有静态部分和实例部分的类型。

比较两个类类型变量时,只有实例成员才会被比较,静态成员和构造函数不在比较范围内。

class Animal {
  feet: number;
  constructor(name: string, numFeet: number) { }
}

class Size {
  feet: number;
  constructor(numFeet: number) {
  }
}

let a: Animal;
let s: Size;

a = s;  // OK
s = a;  // OK

高级类型

前面介绍的那些类型,在开发中都够用了,但是,还有一些高级类型,用的比较少,但也有些场合需要用到

交叉类型

交叉类型听名字像是某几个类型的交集,其实是某几个类型的并集,交叉类型的场景:Mixins等

interface AnyObj {
  [pro: string]: any
}

function extend<F extends AnyObj, S extends AnyObj>(first: F, second: S): F & S {
  const result: Partial<F & S> = Object.assign({}, first, second)
  return <F & S>result
}

interface Person {
  name: string,
  age: number
}

interface Ordered {
  serialNo: number,

  getSerialNo(): number
}

const personA: Person = {
  name: \'Jim\',
  age: 20
}

const orderOne: Ordered = {
  serialNo: 1,
  getSerialNo() {
    return this.serialNo
  }
}
const personOrderd = extend(personA, orderOne)

联合类型

联合类型指定数据可能是某几种类型中的其中一种。

联合类型在实际的应用中很常见,比如某个对象,他的值可能是个元组,也可能是这个类型本身:

interface InfoData {
  [name: string]: {
    [name: string]: [string, string] | InfoData
  }
}

类型别名

类型别名用于给类型起个新名字,能够为原始值、原始数据类型、联合类型、元组、交叉类型等其他任何需要手写的类型:

 

定义一个狮虎兽接口:

interface Lion {
  family: string;
  color: string;
  maneColor: string;
  status: string;
  age: number;
}

interface Tiger {
  stripe: string;
  swimmingSpeed: number;
  treeClimbingSpeed: number;
}

type Liger = Lion & Tiger

提示类型:

type MessageType = \'success\' | \'info\' | \'warning\' | \'error\'

或者交叉类型的联合类型:

type MessageType = \'success\' | \'info\' | \'warning\' | \'error\'
type UserRole = \'admin\' | \'master\' | \'tourist\'
type types = MessageType | UserRole

类型断言

类型单元有两种用法

1.利用as断言

值 as 类型

2.泛型断言

<类型>值

有一个例子:

interface Cat {
  name: string;

  climbing(): void
}

interface Fish {
  name: string;

  swim(): void
}

function isCat(animal: Cat | Fish) {
  //Property \'climbing\' does not exist on type \'Cat | Fish\'.   Property \'climbing\' does not exist on type \'Fish\'.
  if (animal?.climbing ?? false) {
    return true
  }
  return false
}

const fish: Fish = {
  name: \'\',
  swim(): void {
  }
}
console.log(isCat(fish))

可以看到isCat方法中animal?.climbing报错,因为TypeScript不清楚animal的类型到底是Cat还是Fish,要想解决这个问题,我们可以断言animal类型为Cat:

function isCat(animal: Cat | Fish) {
  if ((animal as Cat)?.climbing ?? false) {
    return true
  }
  return false
}

断言的用途

  • 将联合类型断言为其中一种类型
  • 父类断言为更加具体的子类
  • any断言为具体的类型

声明合并

对于相同名称的函数、类、接口的声明,TypeScript会合并为一个函数、类、接口

接口

话不多说,直接上例子:

interface Book {
  long: string;
  wide: string;
  thickness: string;
}

interface Book {
  pageNumber: number;
  author: string;
  press: string;
}

const book: Book = {
  author: \'\',
  long: \'\',
  pageNumber: 0,
  press: \'\',
  thickness: \'\',
  wide: \'\'
}

可以看到,虽然定义了两个Book接口,但是没有报错,而且将对象声明为Book类型时,该对象有两个Book接口的所有属性。

函数


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
vue+typescript入门学习发布时间:2022-07-18
下一篇:
typescript如何用变量获取属性值发布时间:2022-07-18
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap