背景
用TypeScript
重构了一遍业务后盾,TS很大一部分在解决类型标注,根底的类型很容易上手,但到泛型一块,形象水平一下子就高了起来,提供的许多工具也简单了起来。重构的时候始终想整顿一份不便查问的笔记始终没空,当初总算抽出工夫整顿了一份,将于类型无关的局部整顿了进去,其余局部还须要自行翻阅相干文档。
笔记相似纲要,晓得什么状况下有什么能够用,有一个范畴概念,具体细节通过笔记提供的关键字再搜寻相干文档认真理解。
根底类型
TS是JS的超集,所以JS根底的类型都蕴含在内:boolean
、number
、string
、symbol
、array
([]
)、object
({}
)、null
、undefined
TS还提供了其余更加具体的类型标识:any
:任意类型,TS将不对其进行类型检测unknown
:未知类型,在晚期对于未知类型咱们个别标注any
,但标注any
将不会对类型进行检测,unknown
则在对变量第一次赋值后,除了any
类型,其余类型禁止再赋值给此变量,同时在应用其类型所对应的办法时也须要先膨胀类型,能力失常的应用void
:无返回,当一个函数没有返回值的时候此类型标识never
:当函数永远没有返回值的时候用此类型标识。如始终抛出异样的函数,其返回值就是never
tuple
:元组,数组的更准确的标识办法,能够限定数组的数量与每个位的类型enum
:枚举,能够给每一个预约值的数值或是字符串提供扼要的名称,不便编写时辨别... | ...
:类型字面量,如'Red' | 'Green' | 'Blue'
,此时这变量就只能赋值这三种字符串,如果赋值其余字符串时将会报错,也能够是数字字面量 1 | 2 | 3
其余
- 默认状况下
null
和undefined
是所有类型的子类型,能够将这两个值赋值给任意类型。 never
类型也是任何类型的子类型。但其余类型无奈赋值给never
类型,只有never
能力赋值给never
类型。object
与Object
,这两个正文类型只有一个大小写之分,用Object
申明的类型能够拜访Object对象
的默认办法,但object
则不行。
基本概念
: (冒号标注)
TS有主动类型推断,当赋上初始值后TS会生成相应的类型标注,所以个别状况下不用手动增加
let variate: boolean = truelet variate: number = 1let variate: string = 'one'let variate: number[] = [1,3,4,6] // 数组let variate: { first: string, second: number } = { first: 'one', second: 2 } // 对象let sym1 = Symbol('key') // symbollet variate: number[string, number] = ['one', 2] // 元组enum color {Red, Green, Blue} // 枚举
函数参数的类型标注
function func(params: { first: string; second: number;}): void {}const func = function(params: { first: string; second: number;}): void {}
函数的类型标注
const func: (first: string, second: number) => void = function(params) {}const func: (first: string, second: number) => void = () => {} // 这种写法很不容易分辨,只有记录函数的类型标注是必须显示指定返回值的,所以当看到返回值的标注才是类型标注齐全完结
interface (接口)
此是最罕用的类型标注形式。
如上例变量标注也能够用接口代替:
interface IVariate { first: string; second: number;}let variate: IVariate = { first: 'one', second: 2 }
其余个性
?
可选标识符
interface IVariate { first: string; second?: number;}
readonly
只读标识符
interface IVariate { first: string; readonly second: number;}
[key: string | number]: any
任意数量标识
有时候类型数量是动静的,但类型是指定须要束缚的,能够这样写
interface IVariate { first: string; readonly second: number; [key: string]: number;}
extends
接口的继承
interface IVariate { first: string; second: number;}interface IVariate2 extends IVariate { third: string}等价于:interface IVariate2 { first: string; second: number; third: string;}
利用,
能够进行多继承
interface IVariate3 extends IVariate, IVariate2 { fourth: string}
类型合并
interface IVariate { first: string; second: number;}interface IVariate { third: number;}
反复申明类型默认会将申明合并起来,如果有反复定义的类型标注,前面的会把后面的笼罩。
函数
函数参数的类型标注
interface IParams { first: string; second: number;}function func(params: IParams) {}const func = function(params: IParams) {}
函数的类型标注
残缺的标注函数参数以及函数返回值,与参数类型标注一样,只是将定义挪动到interface
中。
interface IFunc { (first: string, second: number): void // 这里须要留神,在interface中函数的类型标注是冒号(:)而不是箭头(=>)}const func: IFunc = function(params) {}const func: IFunc = () => {}
不定参数的类型标注
const func = function(...params: any[]) {}
可选参数的类型标注
须要留神,可选类型之后不能有必选类型。
const func = function(first: string, second?: number) {}
不定参数的类型标注
const func = function(...params: any[]) {}
this
类型标注
因为JS的this
是动静指向的,所以this
类型默认标注为any
,如果须要显式标注能够在函数第一个参数位进行标注
const func = function(this: void, first: string, second?: number) {}
函数重载
当传入不同参数须要返回不同类型时,此时能够用函数重载来标识
function func(data: number): numberfunction func(data: string): stringfunction func(data: number | string): string | number { // 必须要有一个函数反对所有类型 if(typeof data === 'number') return data+1 return data}
类
这一块跟C#很像,根底的元素都搬过去了,能够间接应用。
类的申明
class People { name: string; constructor(name: string) { this.name = name; } hello() { return `Hello ${this.name}`; }}
类的继承
class Student extends People { number: number = 0 constructor(name: string, number: number) { super(name); this.number = number } hello() { return `Hello ${this.name}, Number ${this.number}`; }}
修饰符
修饰符与其余语言一样,public
、private
、protected
。不表明时默认为public
。
动态属性static
,只读readonly
,可选?
。
存取器get
、set
class People2 { private _name: string; constructor(name: string) { this.name = name; } get name(): string { return this._name } set name(newName: string) { this._name = `name: ${newName}` }}
abstract
抽象类 形象办法
标识abstract
关键词,类与办法都不用在定义时被实现,必须在继承的类中实现具体方法
abstract class People3 { abstract hello() {}}
implements
类的接口继承
类的继承只能是单继承,没方法多继承,但理论开发中经常会有一个性能须要多个类应用,这时候能够用接口
interface IStudy { gender: string}interface IGrade { grade(): string}class Student2 extends People implements IStudy, IGrade // implements 能够用逗号分隔指定多个{ number: number = 0 gender: string constructor(name: string, number: number, gender: string) { super(name); this.number = number this.gender = gender } hello() { return `Hello ${this.name}, Number ${this.number}`; } grade(){ return 'A' }}
|
联结类型
通常状况下一个变量一个类型是不够用的,所以会须要指定多种类型,这时候就须要用到联结类型符号
let variate: number | string // 这样就能反对number类型与string类型
&
穿插类型
有时候新的类型是两个旧类型的并集,这时候能够用穿插类型运算符生成新的类型,而不用从新申明
let variate: IVariate & { data: string } // 会生成新的类型,其中蕴含 first second data 三个key
as
、<>
类型断言
<>
断言写在后面<string>variate
,但因为与JSX
语法会有抵触,所以个别应用as
语法,as
语法写在前面variate as string
。
有的场景咱们要应用一个类型的外部值,但TS又会报错时,这时候能够间接强制将类型膨胀。
某些条件下内部传入的值始终合乎预期无需判断,这时候咱们也能够应用断言。
interface IA { first: string; second: number;}interface IB { name: string; age: number;}function func(data: IA | IB) { if((data as IB).name) // ...省略 else return // ...省略}function func2(data: IA) { (data as any as IB).name // 当本来没有标注类型IB时,无奈间接断言,能够应用两次断言强制指定一个类型}
is
关键字
is
能够将类型膨胀成某一类型
function isNumber(x: any): x is number { return typeof x === 'number'}
!
关键字
某些场景类型定义变量是蕴含undefined
的,但咱们应用的时候确定这时候无需判断,能够应用!
断言来打消正告提醒。
interface IVariate { first: string; second: number;}let variate: IVariate | undefinedlet second = variate!.second
type
类型别名
类型别名与interface
一样,但比interface
更加弱小通用。
比方类型字面量想要从新起一个名称方便使用
type color = 'Red' | 'Green' | 'Blue'
甚至能够间接给原始类型起别名
type name = string
或是给interface
再起别名
type funcParams = IParams
也能够应用&
、|
type funcParams2 = IParams & IParams2type funcParams2 = IParams | IParams2
泛型
此前的根底概念大多数相熟面向对象编程语言的能够很快就上手,但到泛型一块,概念性的货色是多了起来,语法也越渐变得复杂起来,TS制订了一套语法,除了写业务逻辑,咱们还须要对JS类型进行编程。
为什么会有泛型呢?在个别应用中,类型绝对固定,是可预期的。但如果要写通用组件时,咱们没方法齐全预期传入的类型和返回类型,有一些返回类型可能是须要用户自定义的,这时候就须要用到泛型来让用户在内部标注类型。
<T>
泛型符号
跟断言很像,但括号这里是在应用时传入的定义类型,命名为T
,其中符号能够随便自定义,但有一些罕用的关键字:T
为type,U
为T的下一位,K
为key,V
为Value,N
为Number,E
为Element
应用时相似这样:
function func<T, U>(arg1: T, arg2: U): [T, U] { return [arg1, arg2]}const <T, U>func = (arg1: T, arg2: U) => [arg1, arg2]class People<T> { name: string; data: T}// 接口中应用泛型interface IArg<T, U> { arg1: T; arg2: U;}
根底泛型工具
keyof
获取键名工具
要解决的类型往往是一个汇合,所以须要有一个工具能够获取汇合中的键名、键值
interface IParams { first: string; second: number;}// 获取键名type keyList = keyof IParams; // "first" | "second"// 获取键值type keyList = IParams[keyof IParams]; // string | number
in
映射工具
in
能够将keyof
每一次循环出的值映射给新的变量。
比方咱们遇到了一个新场景,同样是应用IParams
类型,但其中所有参数是可选的,并非默认必选的,这时候咱们新建一个反复的类型就很麻烦,能够应用in keyof
来将旧类型转换为新类型
// 能够应用type构建咱们解决工具Partialtype Partial<T> = { [P in keyof T]?: T[P]}// 当做泛型,将定义的类型传入type IParamsPartial = Partial<IParams> // 是不是有类型编程的滋味了// 去除可选可用-号标识type Required<T> = { [P in keyof T]-?: T[P]}type IParamsRequired = Required<IParamsPartial>
extends
继承
同样的逻辑,能够用来束缚泛型的格局。用U extends T
U继承自T,具备T中的定义,所以泛型传入的参数必须实现T中的定义
// 咱们束缚传入的值必须带name属性 并且值类型为stinginterface IArg { name: string}function func<T extends IArg>(arg: T){ return arg}// 应用func<{age: number}>({age: 1}) // 当传入泛型的类型不合乎束缚时会提醒谬误 Type '{ age: number; }' does not satisfy the constraint 'IArg'. Property 'name' is missing in type '{ age: number; }' but required in type 'IArg'func<{name: string, age: number}>({name: 'name', age: 1}) // 只有泛型类型加上指定的name: string时才会失常
能够与keyof
组合应用。
比方咱们创立一个函数,第一个参数传入一个对象,第二个参数传入对象的键名,返回此键名对应的值。此时键名参数就是动静的了,写any
无奈达到类型检测的目标,能够应用extends keyof
来进行束缚
function getObjectValue<T, K extends keyof T>(Object: T, key: K) { return Object[key];}const lsit = { a: 1, b: 2, c: 3 }getObjectValue(lsit, 'a'); // 通过 返回1getObjectValue(lsit, 'e'); // 报错 Argument of type '"e"' is not assignable to parameter of type '"a" | "b" | "c"'.
infer
待推断变量工具
infer
必须与extends
联合应用,语句格局T extends (infer U)? true : false
,(infer U)
局部就是咱们填写将要匹配的类型推断主体,类型T
满足类型U
,执行true
中的逻辑,否则执行false
中的逻辑。
手册的例子就很好的展现了多种类型的推断匹配:
type Unpacked<T> = T extends (infer U)[] ? U : // 如果传入的是数组 则将数组的类型命名为U 并且返回U类型 T extends (...args: any[]) => infer U ? U : // 如果传入的是函数 则将函数的返回值命名为U 并且返回U类型 T extends Promise<infer U> ? U : // 如果传入的是Promise 则将Promise的泛型参数命名为U 并且返回U类型 T; // 如果所有皆否 则返回T类型 type T0 = Unpacked<string>; // stringtype T1 = Unpacked<string[]>; // stringtype T2 = Unpacked<() => string>; // stringtype T3 = Unpacked<Promise<string>>; // stringtype T4 = Unpacked<Promise<string>[]>; // Promise<string>type T5 = Unpacked<Unpacked<Promise<string>[]>>; // string
内置泛型工具
TS提供了一些罕用的类型工具。
Partial<T>
(TypeScript 2.1)
将传入的类型的所有属性设置为可选。
type Partial<T> = { [P in keyof T]?: T[P]}
Required<T>
(TypeScript 2.8)
将传入的类型的所有属性设置为必选。
type Required<T> = { [P in keyof T]-?: T[P]}
Readonly<T>
(TypeScript 2.1)
将传入的类型的所有属性设置为只读。
type Readonly<T> = { readonly [P in keyof T]: T[P]}
Record<K, T>
(TypeScript 2.1)
将传入的K类型,从新定义为T类型。
type Record<K extends keyof any, T> = { [P in K]: T;}
Exclude<T, K>
(TypeScript 2.8)
从T类型中排除所有能够赋值给U的类型,生成新类型。
type Exclude<T, U> = T extends U ? never : T;
Extract<T, K>
(TypeScript 2.8)
从T类型中提取所有能够赋值给U的类型,生成新类型。
type Extract<T, U> = T extends U ? T : never;
Pick<T, K>
(TypeScript 2.1)
从T类型中提取K键名的元素,生成新类型。
type Pick<T, K extends keyof T> = { [P in K]: T[P];}
Omit<T, K>
(TypeScript 3.5)
从T类型中排除K键名的元素,生成新类型。
type Omit<T, K> = Pick< T, Exclude<keyof T, K>>
NonNullable<T>
(TypeScript 2.8)
从T类型中排除ull或者undefined类型,生成新类型。
type NonNullable<T> = T extends null | undefined ? never : T;
ReturnType<T>
(TypeScript 2.8)
获取一个函数类型定义的返回类型。
type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;
Parameters<T>
获取一个函数的参数类型。
type Parameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;
ConstructorParameters<T>
获取一个构造函数的参数类型,以数组格局返回。
type ConstructorParameters<T extends new (...args: any[]) => any> = T extends new (...args: infer P) => any ? P : never;
InstanceType<T>
(TypeScript 2.8)
获取一个类的实例类型。
type InstanceType<T extends new (...args: any[]) => any> = T extends new (...args: any[]) => infer R ? R : any;
相干材料
TypeScript文档
TyepScript Handbook
TypeScript Handbook 中文版
TypeScript Deep Dive
TypeScript Deep Dive 中文版
TypeScript入门教程
TypeScript Web版
TypeScript 内置工具泛型