interfaceLengthwise{length:number}functionloggingIdentity<TypeextendsLengthwise>(arg:Type):Type{console.log(arg.length)// Now we know it has a .length property, so no more errorreturnarg}
由于上面的这种类型约束可能过于强大了,例如要求Type必须含有类型为 number 的属性 length,而有时候其实不需要约束 length 的类型。所以TypeScript加入了 keyof 关键词来约束属性:
TypeScript
functiongetProperty<Type,KeyextendskeyofType>(obj:Type,key:Key){returnobj[key]}letx={a:1,b:2,c:3,d:4}getProperty(x,'a')// getProperty(x, 'm')// Argument of type ''m'' is not assignable to parameter of type ''a' | 'b' | 'c' | 'd''.
declarefunctionstringOrNum(x:string):numberdeclarefunctionstringOrNum(x:number):stringdeclarefunctionstringOrNum(x:string|number):string|numbertypeT1=ReturnType<typeofstringOrNum>// T1 = string | number
如果函数有多个重载,则一般会使用最通用的(最后一个)调用签名。
keyof
keyof操作可以获得属性的字面值,并以或的形式结合在一起:
TypeScript
typePoint={x:numbery:number}typeP=keyofPoint// type P = “x” | “y”
如果类型别名或者接口中含有索引签名,则 keyof 的结果是索引签名的索引类型(实际上只有 sting,或者 string | number 两种情况)。
可以根据属性名获得其类型:
TypeScript
typePerson={age:numbername:stringalive:boolean}typeAge=Person['age']typeI1=Person['age'|'name']// type I1 = string | numbertypeAliveOrName='alive'|'name'typeI3=Person[AliveOrName]// type I3 = string | boolean
可以通过 keyof 获得类型内的属性名,进而获得对应的类型:
TypeScript
typeI4=keyofPerson// type I4 = 'age' | 'name' | 'alive'typeI2=Person[keyofPerson]// type I2 = string | number | boolean
也可以通过 keyof 获得数组成员的类型:
TypeScript
constMyArray=[{name:'Alice',age:15},{name:'Bob',age:23},{name:'Eve',age:38},]typePerson=typeofMyArray[number]// type Person = {// name: string// age: number// }typeAge=typeofMyArray[number]['age']
条件类型
TypeScript中可以为变量添加条件,类型自然也可以添加条件:
TypeScript
letx=true?'string':1// x: string | numberinterfaceAnimal{live():void}interfaceDogextendsAnimal{woof():void}typeExample1=DogextendsAnimal?number:string// type Example1 = numbertypeExample2=RegExpextendsAnimal?number:string// type Example2 = string
typeMessageOf<T>=Textends{message:unknown}?T['message']:neverinterfaceEmail{message:string}interfaceDog{bark():void}typeEmailMessageContents=MessageOf<Email>// type EmailMessageContents = stringtypeDogMessageContents=MessageOf<Dog>// DogMessageContents = never
typeFlatten<T>=Textendsany[]?T[number]:T// Extracts out the element type.typeStr=Flatten<string[]>// Str = string// Leaves the type alone.typeNum=Flatten<number>// Num = numbertypeFlatten<Type>=TypeextendsArray<inferItem>?Item:Type// 使用infer关键词typeNum=GetReturnType<()=>number>// Num = number
typeStrArrOrNumArr=ToArray<string|number>// type StrArrOrNumArr = string[] | number[]typeToArrayNonDist<Type>=[Type]extends[any]?Type[]:never// 'StrArrOrNumArr' is no longer a union.typeStrArrOrNumArr=ToArrayNonDist<string|number>// StrArrOrNumArr = (string | number)[]
为了阻止这一点,可以使用方括号来保护类型的完整性。不过个人觉得可以使用参数括起来,或者通过构造一个不使用联合的 type 来阻止这一点:
typePropEventSource<Type>={on(eventName:`${string&keyofType}Changed`,callback:(newValue:any)=>void):void}/// Create a 'watched object' with an 'on' method/// so that you can watch for changes to properties.declarefunctionmakeWatchedObject<Type>(obj:Type):Type&PropEventSource<Type>constperson=makeWatchedObject({firstName:'Saoirse',lastName:'Ronan',age:26})person.on('firstNameChanged',()=>{})