1. 类型断言
类型断言(Type Assertion)可以用来手动指定一个值的类型。语法值 as 类型。
2. 断言应用
将一个联合类型断言为其中一个类型。
interface Cat {name: string;run(): void;}interface Fish {name: string;swim(): void;}function getName(animal: Cat | Fish) {return animal.name;}function isFish(animal: Cat | Fish) {if (typeof (animal as Fish).swim === 'function') {return true;} else {return false;}}function swim(animal: Cat | Fish) {//不恰当断言(animal as Fish).swim();}const tom: Cat = {name: 'Tom', run() {console.log('run');}}//编译通过,运行出错,Cat无 swim()函数swim(tom);//- Uncaught TypeError: animal.swim is not a function`
将父类断言为一个子类。
class ApiError extends Error {code: number = 0;}class HttpError extends Error {statusCode: number = 200;}//断言,通过typeof 来判断类型function isApiError(error: Error) {if (typeof (error as ApiError).code === 'number') {return true;} else {return false;}}//通过instanceof 来判断error是否是ApiError的实例function isApiErrorOther(error: Error) {if (error instanceof ApiError) {return true;} else {return false;}}
在接口使用中,使用断言
interface ApiError extends Error {code: number;}interface HttpError extends Error {statusCode: number;}function isApiErrorLast(error: Error) {if (typeof (error as ApiError).code === 'number') {return true;} else {return false;}}
将任何一个类型断言为**any**
//给window添加一个属性,如果不用断言,这会编译出错(window as any).foo = 1 ;
在上述示例中,需要注意的是,将一个变量断言为any是解决TypeScript中类型问题的最后一个手段。他极有可能掩盖了真正的类型错误,所以如果不是非常确定的情况下,不建议使用 **as any**。
将**any**断言为一个具体的类型
function getCacheData(key: string): any {return (window as any).cache[key];}interface Cat{name: string ;run():void ;}//将any类型断言为具体类型const t = getCacheData('tom') as Cat ;t.run() ;
上述示例中,我们调用完getCacheData之后,立即将其断言为Cat类型,这样明确了t的类型,提高了代码的可维护性。
总结:
- 联合类型可以被断言为其中一个类型
- 父类可以被断言为子类
- 任何类型都可以被断言为
any any可以被断言为任何特定类型- 需要
A能够断言为B,只需要A兼容B或者B兼容A即可。
子类父类相互断言
interface Animal {name: string;}interface Cat extends Animal {run(): void;sleep(): void;}function testAnimal(animal: Animal) {return animal as Cat;}function testCat(cat: Cat) {return cat as Animal;}
双重断言
interface Cat {run():void ;}interface Fish{swim():void ;}//双重断言function testCatOther(cat: Cat){return cat as any as Fish ;}
上述示例:如果直接使用cat as Fish肯定会提示错误,因为Cat和Fish相互都不兼容,但若使用双重断言,则可以打破【要使得 A能够被断言为 B,只需要 A兼容 B或 B 兼容 A 即可】的限制,将任何一个特定类型断言为另外一个任何特定类型。若使用了双重断言,那么大部分情况是错误的,所以尽量避免使用双重断言
类型断言 vs 类型转换
类型断言只会影响TypeScript编译时的类型,类型断言语句在编译结果总会被删除。
function toBoolean(some: any): boolean {return some as boolean;}toBoolean(1) ;//返回值为1function toBooleanOther(some: any):boolean{return Boolean(some) ;}toBooleanOther(1) ;//返回值为true
function getCacheDataOther(key: string): any {return (window as any).cache[key];}interface Cat {name: string;run(): void;}//将any类型转换为Catconst c: Cat = getCacheDataOther('tom');c.run();
上述示例中,通过类型声明的方式,将c声明为Cat,然后将any类型的数据 getCacheData('tom')赋值给Cat类型的c。
类型断言 vs 类型转换
interface Animal {name: string;}interface Cat extends Animal {run(): void;}const animal: Animal = {name: 'tom'}//类型转换会编译出错let a: Cat = animal;//编译错误- error TS2741: Property 'run' is missing in type 'Animal' but required in type 'Cat'.
上述示例中,不允许将animal赋值为Cat类型的a,因为Animal是Cat的父类,当然不能将父类的实例赋值给子类变量。
类型断言和类型转换的核心区别在于:
animal断言为Cat,只需啊哟满足Animal兼容Cat或者Cat兼容Animal即可。animal赋值给a,则需要Cat兼容Animal才行,但是Cat并不兼容Animal。- 类型声明比类型断言更加严格。
类型断言 vs 泛型
function getCacheDataLast<T>(key:string) :T {return (window as any).cache[key] ;}interface Cat{name: string ;run():void ;}const s = getCacheDataLast<Cat>('tom') ;s.run() ;
通过给getCacheDataLast函数添加一个泛型<T>,我们可以更加规范的实现对getCacheDataLast返回值的约束,也同时去掉代码中的any,此方法是最优解决方案。
