类型断言有如下案例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
interface Cat {
name: string;
run(): void;
}
interface Fish {
name: string;
swim(): void;
}
function getName(animal: Cat | Fish): string {
return animal.name;
}
function isFish(animal: Cat | Fish): boolean {
// 我觉得这种写法有点怪,在java中,我们会直接判断animal是否是Fish的实例类型
return typeof (animal as Fish).swim === 'function';
}
|
类型断言只能欺骗TypeScript编译器,无法避免运行时错误,滥用类型断言可能会导致运行时错误:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
interface Cat {
name: string;
run(): void;
}
interface Fish {
name: string;
swim(): void;
}
function swim(animal: Cat | Fish) {
(animal as Fish).swim();
}
const tom: Cat = {
name: 'Tom',
run() { console.log('run') }
};
swim(tom);
|
类型断言也用于继承关系:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class ApiError extends Error {
code: number = 0;
}
class HttpError extends Error {
statusCode: number = 200;
}
function isApiError(error: Error): boolean {
return typeof (error as ApiError).code === 'number';
}
|
什么时候使用instanceof
如果ApiError和HttpError是一个类,则使用instanceof更合适:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class ApiError extends Error {
code: number = 0;
}
class HttpError extends Error {
statusCode: number = 200;
}
function isApiError(error: Error): boolean {
return error instanceof ApiError;
}
|
如果ApiError和HttpError不是一个真正的类,而是一个TypeScript接口。因为接口是一个类型,不是一个真正的值,它在编译结果中会被删除,所以就无法使用instanceof来做运行时判断了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
interface ApiError extends Error {
code: number;
}
interface HttpError extends Error {
statusCode: number;
}
function isApiError(error: Error) {
// 编译就开始报错了
return error instanceof ApiError;
}
|
这个时候就使用通过类型断言,判断是否存在code属性,来判断传入的参数是不是ApiError了。
什么时候断言为any
例如我们需要给window上加一个属性:
1
2
3
|
(window as any).foo = 1;
|
这种写法有可能掩盖了真正的类型错误,所以如果不是非常确定,就不要使用as any。上面的例子可以拖过扩展Windows类型来解决(我对这部分非常的陌生)。
类型断言与类型声明
两种写法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
function getCacheData(key: string): any {
return (window as any).cache[key];
}
interface Cat {
name: string;
run(): void;
}
// 类型断言
const tom = getCacheData('tom') as Cat;
tom.run();
// 类型声明
const tom: Cat = getCacheData('tom');
tom.run();
|
理解这种差距,可以通过如下这个案例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
interface Animal {
name: string;
}
interface Cat {
name: string;
run(): void;
}
const animal: Animal = {
name: 'tom'
};
// 编译通过
let tom = animal as Cat;
// 编译不通过
let tom: Cat = animal;
|
深入理解(我还是理解不了):
- animal断言为Cat,只需要满足Animal兼容Cat或Cat兼容Animal即可
- animal赋值为tom,需要满足Cat兼容Animal才行,但是案例中Cat并不兼容Animal
类型声明比类型断言更加严格,且比类型断言的as语法更优雅。
类型断言与泛型
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function getCacheData(key: string): any {
return (window as any).cache[key];
}
interface Cat {
name: string;
run(): void;
}
const tom = getCacheData<Cat>('tom')
|
泛型是最优的解决方案。