functionprintCoord(pt:{x:number;y:number}){console.log('The coordinate'sxvalueis' + pt.x)
console.log('Thecoordinate's y value is '+pt.y)}printCoord({x:3,y:7})
使用的时候传入一个初始化器即可。
TypeScript
functionprintName(obj:{first:string;last?:string}){// Error - might crash if 'obj.last' wasn't provided!// console.log(obj.last.toUpperCase())// Object is possibly 'undefined'.if (obj.last!==undefined){// OKconsole.log(obj.last.toUpperCase())}// A safe alternative using modern JavaScript syntax:console.log(obj.last?.toUpperCase())}
functionprintId(id:number|string){if (typeofid==='string'){// In this branch, id is of type 'string'console.log(id.toUpperCase())}else{// Here, id is of type 'number'console.log(id)}}
constreq={url:'https://example.com',method:'GET'}handleRequest(req.url,req.method)// Argument of type 'string' is not assignable to parameter of type ''GET' | 'POST''.
为了解决这个问题,有两种方式:
TypeScript
// Way 1:// Change 1:constreq={url:'https://example.com',method:'GET'as 'GET'}// Change 2handleRequest(req.url,req.methodas 'GET')// Way 2:constreq={url:'https://example.com',method:'GET'}as consthandleRequest(req.url,req.method)
这种方式只适用于直接属于或者具有继承关系的情况,不适用于使用 type 和 interface 定义的类型别名和接口。
类型谓词
和C++类似的是,一个普通TypeScript函数并不能当作静态编译期谓词使用,TypeScript中如果想要自定义一个类型谓词,需要使用 is 关键词:
TypeScript
typeFish={swim:()=>void}typeBird={fly:()=>void}functionisFish(pet:Fish|Bird):petisFish{//return (pet as Fish).swim !== undefinedreturn'swim'inpet}functiontest(pet:Fish|Bird){if (isFish(pet)){pet.swim()}else{pet.fly()}}
pet is Fish 是一个类型谓词,pet 必须是参数列表中的参数。isFish 函数通过另一个谓词判断参数的真实类型,然后返回。TypeScript官网的教程使用的是被注释掉的写法,不知道为什么用了比较丑的方式,可能是为了说明可以将 pet 转换为 Fish 在一些情况中是合法的。
never 类型
如果经过窄化后的分支永远不可达,则该分支上的对象具有 never 类型。never 类型只存在于TypeScript的类型系统中,不存在于JavaScript。
任何类型的对象都可以被 never 赋值,但是 never 类型的对象只能被 never 赋值,利用这个性质可以设计出一个禁止对参数类型进行扩展的函数:
TypeScript
interfaceCircle{kind:'circle'radius:number}interfaceSquare{kind:'square'sideLength:number}interfaceTriangle{kind:'triangle'sideLength:number}typeShape=Circle|Square;// type Shape = Circle | Square | TrianglefunctiongetArea(shape:Shape){switch (shape.kind){case'circle':returnMath.PI*shape.radius**2case'square':returnshape.sideLength**2default:const_exhaustiveCheck:never=shape// Type 'Triangle' is not assignable to type 'never'.return_exhaustiveCheck}}
functionminimumLength<Typeextends{length:number}>(obj:Type,minimum:number):Type{if (obj.length>=minimum){returnobj}else{return{length:minimum}// error// Type '{ length: number; }' is not assignable to type 'Type'.}}
虽然 { length: number; } 满足类型约束,是 Type 的子类型,但是仍然不是 Type。
可选参数和默认实参
TypeScript支持可选参数,个人认为这应该是 type = Type | undefined 的语法糖:
TypeScript
functionf(x?:number){if (x!==undefined){// do x}}functionfd(x=10){// ...}
functionmultiply(n:number,...m:number[]){returnm.map((x)=>n*x)}// 'a' gets value [10, 20, 30, 40]consta=multiply(10,1,2,3,4)constarr1=[1,2,3]constarr2=[4,5,6]arr1.push(...arr2)// Inferred as 2-length tupleconstargs=[8,5]as const// OKconstangle=Math.atan2(...args)
注意,数组默认是可变的,有时候需要转换为字面值才能使用。
参数解构
TypeScript支持将类的属性解散以方便的使用,类似于C++的结构化绑定:
TypeScript
functionsum({a,b,c}:{a:number;b:number;c:number}){console.log(a+b+c)}// Same as prior exampletypeABC={a:number;b:number;c:number}functionsum({a,b,c}:ABC){console.log(a+b+c)}
readonly
type 和 interface 可以定义只读的属性:
TypeScript
interfaceSomeType{readonlyprop:string}
注意,readonly 只修饰其直接修饰的对象,而不修饰对象的属性。
索引签名
索引签名用于描述k-v型的结构:
TypeScript
interfaceStringByString{[key:string]:string}constheroesInBooks:StringByString={'Gunslinger':'The Dark Tower','Jack Torrance':'The Shining'};interfaceOptions{[key:string]:string|number|booleantimeout:number}constoptions:Options={timeout:1000,timeoutMessage:'The request timed out!',isFileUpload:false};