classBird{fly(){console.log('Bird flying');}layEggs(){console.log('Bird layEggs');}}classFish{swim(){console.log('Fish swimming');}layEggs(){console.log('Fish layEggs');}}const bird =newBird();const fish =newFish();functionstart(pet: Bird | Fish){// 调用 layEggs 没问题,因为 Bird 或者 Fish 都有 layEggs 方法
pet.layEggs();// 会报错:Property 'fly' does not exist on type 'Bird | Fish'// pet.fly();// 会报错:Property 'swim' does not exist on type 'Bird | Fish'// pet.swim();}start(bird);start(fish);
二、关键字
类型约束(extends)
语法:T extends K
这里的 extends 不是类、接口的继承,而是对于类型的判断和约束,意思是判断 T 能否赋值给 K
可以在泛型中对传入的类型进行约束
const copy =(value:string|number):string|number=> value
// 只能传入 string 或者 numbercopy(10)// 会报错:Argument of type 'boolean' is not assignable to parameter of type 'string | number'// copy(false)
functionstart(pet: Bird | Fish){// 调用 layEggs 没问题,因为 Bird 或者 Fish 都有 layEggs 方法
pet.layEggs();if((pet as Bird).fly){(pet as Bird).fly();}elseif((pet as Fish).swim){(pet as Fish).swim();}}
但是这样做,判断以及调用的时候都要进行类型转换,未免有些麻烦,可能会想到写个工具函数判断下:
functionisBird(bird: Bird | Fish):boolean{return!!(bird as Bird).fly;}functionisFish(fish: Bird | Fish):boolean{return!!(fish as Fish).swim;}functionstart(pet: Bird | Fish){// 调用 layEggs 没问题,因为 Bird 或者 Fish 都有 layEggs 方法
pet.layEggs();if(isBird(pet)){(pet as Bird).fly();}elseif(isFish(pet)){(pet as Fish).swim();}}
TypeScript 不仅知道在 if 分支里 pet 是 Fish 类型; 它还清楚在 else 分支里,一定不是 Fish 类型,一定是 Bird 类型
待推断类型(infer)
可以用 infer P 来标记一个泛型,表示这个泛型是一个待推断的类型,并且可以直接使用
比如下面这个获取函数参数类型的例子:
typeParamType<T>=Textends(param: infer P)=>any?P:T;typeFunctionType=(value:number)=>booleantypeParam= ParamType<FunctionType>;// type Param = numbertypeOtherParam= ParamType<symbol>;// type Param = symbol
判断 T 是否能赋值给 (param: infer P) => any,并且将参数推断为泛型 P,如果可以赋值,则返回参数类型 P,否则返回传入的类型
再来一个获取函数返回类型的例子:
typeReturnValueType<T>=Textends(param:any)=> infer U?U:T;typeFunctionType=(value:number)=>booleantypeReturn= ReturnValueType<FunctionType>;// type Return = booleantypeOtherReturn= ReturnValueType<number>;// type OtherReturn = number
判断 T 是否能赋值给 (param: any) => infer U,并且将返回值类型推断为泛型 U,如果可以赋值,则返回返回值类型 P,否则返回传入的类型
原始类型保护(typeof)
语法:typeof v === "typename" 或 typeof v !== "typename"
functionstart(pet: Bird | Fish){// 调用 layEggs 没问题,因为 Bird 或者 Fish 都有 layEggs 方法
pet.layEggs();if((pet as Bird).fly){(pet as Bird).fly();}elseif((pet as Fish).swim){(pet as Fish).swim();}}
const person ={
name:'Jack',
age:20}functiongetPersonValue<Textendskeyoftypeof person>(fieldName:keyoftypeof person){return person[fieldName]}const nameValue =getPersonValue('name')const ageValue =getPersonValue('age')// 会报错:Argument of type '"gender"' is not assignable to parameter of type '"name" | "age"'// getPersonValue('gender')
索引访问操作符(T[K])
语法:T[K]
类似于 js 中使用对象索引的方式,只不过 js 中是返回对象属性的值,而在 ts 中返回的是 T 对应属性 P 的类型
interfacePerson{
name:string
age:number}const person: Readonly<Person>={
name:'Lucy',
age:22}// 会报错:Cannot assign to 'name' because it is a read-only property
person.name ='Lily'
interfaceReadonlyArray<T>{/** Iterator of values in the array. */[Symbol.iterator](): IterableIterator<T>;/**
* Returns an iterable of key, value pairs for every entry in the array
*/entries(): IterableIterator<[number,T]>;/**
* Returns an iterable of keys in the array
*/keys(): IterableIterator<number>;/**
* Returns an iterable of values in the array
*/values(): IterableIterator<T>;}
只能在数组初始化时为变量赋值,之后数组无法修改
使用:
interfacePerson{
name:string}const personList: ReadonlyArray<Person>=[{ name:'Jack'},{ name:'Rose'}]// 会报错:Property 'push' does not exist on type 'readonly Person[]'// personList.push({ name: 'Lucy' })// 但是内部元素如果是引用类型,元素自身是可以进行修改的
personList[0].name ='Lily'
可选类型(Partial<T>)
用于将 T 类型的所有属性设置为可选状态,首先通过 keyof T,取出类型 T 的所有属性, 然后通过 in 操作符进行遍历,最后在属性后加上 ?,将属性变为可选属性。
定义:
typePartial<T>={[PinkeyofT]?:T[P];}
用法:
interfacePerson{
name:string
age:number}// 会报错:Type '{}' is missing the following properties from type 'Person': name, age// let person: Person = {}// 使用 Partial 映射后返回的新类型,name 和 age 都变成了可选属性let person: Partial<Person>={}
person ={ name:'pengzu', age:800}
person ={ name:'z'}
person ={ age:18}
必选类型(Required<T>)
和 Partial 的作用相反
用于将 T 类型的所有属性设置为必选状态,首先通过 keyof T,取出类型 T 的所有属性, 然后通过 in 操作符进行遍历,最后在属性后的 ? 前加上 -,将属性变为必选属性。
定义:
typeRequired<T>={[PinkeyofT]-?:T[P];}
使用:
interfacePerson{
name?:string
age?:number}// 使用 Required 映射后返回的新类型,name 和 age 都变成了必选属性// 会报错:Type '{}' is missing the following properties from type 'Required<Person>': name, agelet person: Required<Person>={}
/**
* Obtain the parameters of a constructor function type in a tuple
*/typeConstructorParameters<Textendsnew(...args:any)=>any>=Textendsnew(...args: infer P)=>any?P:never;
/**
* Obtain the return type of a constructor function type
*/typeInstanceType<Textendsnew(...args:any)=>any>=Textendsnew(...args:any)=> infer R?R:any;