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) ;
//返回值为1
function 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类型转换为Cat
const 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
,此方法是最优解决方案。