原始数据类型 :meat_on_bone: JS中数据类型分两类:原始数据类型和引用类型。原始数据类型包括布尔值、数值、字符串、null、undefined和es6中的新类型Symbol、BigInt。
布尔值的使用
1 let isDone: boolean = false ;
模板字符串的使用
1 2 3 4 5 6 let myName: string = 'Tom' ;let myAge: number = 25 ;let sentence: string = `Hello, my name is ${myName} . I'll be ${myAge + 1 } years old next month.` ;
:zero: 空值,当函数没有返回值时使用
1 2 3 function alertName ( ): void { alert('My name is Tom' ); }
任意值 允许被赋值为任意类型,可以访问任意值的任何属性和任何方法。可以认为对任意值的任何操作,返回的类型都是任意值。
如果声明时没有指定其类型,会被识别为任意值类型。
类型推论 当定义时没有明确指定类型时,TS会根据后面的赋值自动推断出一个类型。如果定义时没有赋值,会被推断成any
联合类型 表示取值可以为多种类型的一个
1 2 3 let myFavoriteNumber: string | number ; myFavoriteNumber = 'seven' ; myFavoriteNumber = 7 ;
当访问联合类型的属性或方法时,注意只能访问它们共有的属性或方法。
当被赋值的时候,会根据类型推论判断出一个类型。
接口 接口是对行为的抽象,也用于对对象的形状 进行描述。对象的形状必须与接口的形状一致,接口一般首字母大写,有的会加上I 。
可选、任意、只读属性 可选属性可以不一定完全匹配接口的形状,任意属性指属性的类型可以是确定属性或可选属性中的某一个,只读属性用readonly定义,只能在创建的时候被赋值。:mushroom:
1 2 3 4 5 6 7 8 9 10 interface Person { name: string ; age?: number ; [propName: string ]: string | number ; readonly id: number ; }let tom: Person = { name: 'Tom' };
数组的类型 TS中数组有多种定义方式。
类型+方括号[] 1 let fibonacci: number [] = [1 , 1 , 2 , 3 , 5 ];
数组的项中不允许出现其他的类型,包括调用方法添加项,比如说push方法只能添加number类型
数组泛型 使用Array<elemType>
来表示数组
1 let fibonacci: Array <number > = [1 , 1 , 2 , 3 , 5 ];
接口表示 1 2 3 4 interface NumberArray { [index: number ]: number ; }let fibonacci: NumberArray = [1 , 1 , 2 , 3 , 5 ];
可以描述数组,但一般不会这么做,比较复杂。但可以用在描述类数组中。
类数组 比如说arguments,应该这样定义
1 2 3 4 5 6 7 function sum ( ) { let args: { [index: number ]: number ; length: number ; callee: Function ; } = arguments ; }
事实上常用的类数组都有自己的接口定义,如 IArguments
, NodeList
, HTMLCollection
其中 IArguments
是 TypeScript 中定义好了的类型,它实际上是一个内置对象👇
1 2 3 4 5 interface IArguments { [index: number ]: any ; length: number ; callee: Function ; }
any在数组中 用any表示数组中允许出现任意类型
1 let list: any[]=['qingcheng' ,12 ,{name: 'xumeijin' }]
函数的类型 JS中定义函数的方法有两种,函数声明,函数表达式,在TS中要对函数的输入输出进行约束
函数声明 1 2 3 function sum (x: number , y: number ): number { return x + y; }
函数表达式 1 2 3 let mySum = function (x: number , y: number ): number { return x + y; };
这样写,左边是根据类型推论推断出来的。完整写法如下👇
1 2 3 let mySum: (x: number , y: number ) => number = function (x: number , y: number ): number { return x + y; };
注意这里的=>表示函数的定义,不是es6中的箭头函数
1 2 3 4 5 6 7 interface SearchFunc { (source: string , subString: string ): boolean ; }let mySearch: SearchFunc; mySearch = function (source: string , subString: string ) { return source.search(subString) !== -1 ; }
可选参数 与前面的可选属性类似,用?表示,需要注意可选参数必须在必需参数后面。
参数默认值 TS将添加了默认值的参数识别为可选参数,此时不受可选参数必须在必需参数后面 的约束 :happy:
1 2 3 4 5 function buildName (firstName: string = 'Tom', lastName: string ) { return firstName + ' ' + lastName; }let tomcat = buildName('Tom' , 'Cat' );let cat = buildName(undefined , 'Cat' );
ES6也可以设置函数默认参数
剩余参数 ES6中可以用…rest获取剩余参数,TS也支持。:surfing_woman:
1 2 3 4 5 6 7 8 function push (array: any[], ...items: any[] ) { items.forEach(function (item ) { array.push(item); }); }let a = []; push(a, 1 , 2 , 3 );
注意rest参数只能是最后一个参数
重载 允许一个函数接受不同数量或类型的参数时,走不同的逻辑。利用联合类型,可以这样实现👇
1 2 3 4 5 6 7 function reverse (x: number | string ): number | string { if (typeof x === 'number' ) { return Number (x.toString().split('' ).reverse().join('' )); } else if (typeof x === 'string' ) { return x.split('' ).reverse().join('' ); } }
还可以更精确地表示,让输出与输入的类型一致 :star2:
1 2 3 4 5 6 7 8 9 function reverse (x: number ): number ;function reverse (x: string ): string ;function reverse (x: number | string ): number | string { if (typeof x === 'number' ) { return Number (x.toString().split('' ).reverse().join('' )); } else if (typeof x === 'string' ) { return x.split('' ).reverse().join('' ); } }
类型断言 用于手动指定一个值的类型:crossed_fingers:,语法如下
在tsx中必须用前者,由于形如 <Foo>
的语法在 tsx 中表示的是一个 ReactNode
,在TS中也可以表示为一个泛型。所以建议使用前者。
类型断言常见类型如下
对联合类型断言 之前提到过,当不确定联合类型的类型时候,只能访问公共的属性或方法。但是当我们不确定类型也想访问里面的方法咋办。
这时可以用到断言
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 interface Cat { name: string ; run(): void ; }interface Fish { name: string ; swim(): void ; }function isFish (animal: Cat | Fish ) { if (typeof (animal as Fish).swim === 'function' ) { return true ; } return false ; }
但是断言只能影响TS的编译,如果逻辑错了运行时会报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 interface Cat { name: string ; run(): void ; }interface Fish { name: string ; swim(): void ; }function swim (animal: Cat | Fish ) { (animal as Fish).swim(); }const tom: Cat = { name: 'Tom' , run() { console .log('run' ) } }; swim(tom);
所以尽量避免断言后调用方法或引用深层属性,以减少不必要的运行时错误🔒
父类断言为具体子类 当类之间有继承关系时,通过断言父类来判断子类类型
1 2 3 4 5 6 7 8 9 10 11 12 13 class ApiError extends Error { code: number = 0 ; }class HttpError extends Error { statusCode: number = 200 ; }function isApiError (error: Error ) { if (typeof (error as ApiError).code === 'number' ) { return true ; } return false ; }
以上的例子也可以用instanceof来判断,因为 ApiError
是一个 JavaScript 的类,但是当ApiError
为一个接口时,使用typeof更加合适。
对任何类型断言为any 当我们非常确定这段代码不会出错,比如在window下添加foo属性
直接这么写 TypeScript 编译时会报错,提示我们 window
上不存在 foo
属性。所以需要使用断言
1 (window as any ).foo = 1 ;
TypeScript 的设计理念 之一:一方面不能滥用 as any
,另一方面也不要完全否定它的作用,我们需要在类型的严格性和开发的便利性之间掌握平衡 :balance_scale:
内置对象 JS本身就有很多内置对象,在TS中可以当作已经定义好了的类型
ECMAScript标准里的内置对象:Boolean、Error、Date、RegExp
DOM,BOM中的内置对象:Document、HTMlElement、Event、NodeList
可以将变量定义为这些类型。
Node.js 不是内置对象的一部分,如果想用 TypeScript 写 Node.js,则需要引入第三方声明文件:
1 npm install @types/node --save-dev