参考资料:https://ts.xcatliu.com/basics/type-assertion.html
最近在研究TypeScript,现在很多模块都是用这个写的,不懂也不方便看源码呐,所以随手做下笔记(我一直觉得写笔记是一个很好的学习方法,虽然会花费较长的时间,但写的过程却可以加深印象)。
类型断言用于手动指定一个值的类型。
值 as 类型
interface Cat {
name:string;
run():void;
}
interface Fish {
name:string;
swim():void;
}
function getName(animal:Cat|Fish):string{
return animal.name;
}
只能访问联合属性中共有的属性和方法——name
。
如果在不确定类型的时候就想访问一个类型确定的属性和方法,就需要【断言】:
function isFish(animal:Cat|Fish):boolean{
return (typeof (animal as Fish).swim) === "function";
}
将变量animal
断言为Fish
类型,那么访问其swim
属性,就可以通过编译器的检查。
类型断言相当于欺骗编译器,编译的时候不报错,不代表运行的时候不报错。
const tom:Cat = {
name:"cat",
run(){
console.log("running");
}
}
function swim(animal:Cat|Fish){
(animal as Fish).swim();
}
swim(tom);
编译结果:
var tom = {
name: "cat",
run: function () {
console.log("running");
}
};
function swim(animal) {
animal.swim();
}
swim(tom);
//TypeError: animal.swim is not a function
class ApiError extends Error{
code:number = 0;
}
class HttpError extends Error{
statusCode:number = 200;
}
function isApiError(error:Error){
return (typeof (error as ApiError).code === "number");
}
父类Error
并没有code
属性,会直接报错,所以需要将其断言为子类ApiError
。
上例用instanceof判断似乎更为合适,如果ApiError
、HttpError
为接口,那么就只能用断言来判断了:
interface ApiError extends Error{
code:number;
}
interface HttpError extends Error{
statusCode:number;
}
window.a = "hello world";
//Property 'a' does not exist on type 'Window & typeof globalThis'
只是想给window
添加一个属性,但因为window
上并没有a
属性,所以报错。此时,就需要将window
断言为any
:
(window as any).a = "hello world";
不能滥用as any
,也不能完全否定它的作用,需要在类型的严格性和开发的便利性之间掌握平衡。
function getCacheData(key: string): any {
return (window as any).cache[key];
}
interface Cat {
name:string;
run():void;
}
const tom = getCacheData("tom") as Cat;
getCacheData
执行以后返回的是any
类型,在赋值变量tom
时候可以将其断言为Cat
类型。后续对tom
的访问时就有了代码补全,提高了代码的可维护性。
综上所述,类型断言有以下特点:
any
any
可以被断言为任何类型但类型之间的断言却是有限制的。若A能兼容B,那么可以将A断言为B,也可以将B断言为A。
interface Animal {
name:string;
}
interface Cat {
name:string;
run():void;
}
const tom:Cat = {
name:"tom",
run(){
console.log("running");
}
}
const animal:Animal = tom;
TS是结构类型系统,类型之间的对比只会比较它们最终的结构,而会忽略它们定义时的关系。所以在上例中,尽管Animal
、Cat
定义时并没有任何关系,但结构上可以理解为Cat
继承于Animal
:
interface Cat extends Animal{
run():void;
}
即:Animal
兼容Cat
。此时二者可以互相断言:
function testAnimal(animal:Animal){
return animal as Cat
}
function testCat(cat:Cat){
return cat as Animal;
}
animal as cat
是因为父类可以被断言为子类。cat as animal
是因为子类拥有父类的所有属性和方法,调用也不会出问题。综上所述:
any
any
可以被断言为任何类型前四种情况都是最后一种的特例。
既然:
any
any
可以被断言为任何类型那么类型岂不可以互相断言了?
const a:number = 666;
const b:string = a as any as string;
但这么做反而会导致一系列问题,所以,不到万不得已,最好不要这么做。
类型断言只会影响TS编译时的类型,类型断言语句会在编译结果中删除。
function toBoolean(something: any): boolean {
return something as boolean;
}
toBoolean(1);
// 返回值为 1
而类型转换则会影响编译结果:
function toBoolean(something: any): boolean {
return Boolean(something);
}
toBoolean(1);
// 返回值为 true
类型断言:
function getCacheData(key: string): any {
return (window as any).cache[key];
}
interface Cat {
name: string;
run(): void;
}
const tom = getCacheData('tom') as Cat;
类型申明:
const tom:Cat = getCacheData('tom');
因为getCacheData()
的返回类型是any
,所以这个例子二者效果是一样的。但二者还是有区别的:
interface Animal{
name:string;
}
interface Cat {
name:string;
run():void;
}
const animal:Animal = {
name:"tom"
}
const tom = animal as Cat;
但类型声明却不行:
const tom:Cat = animal;
//Property 'run' is missing in type 'Animal' but required in type 'Cat'
Animal
兼容Cat
,所以可以将animal
断言为Cat
,赋值给tom
。但直接申明tom
为Cat
类型是不可以的。
深入的讲,它们的核心区别就在于:
animal
断言为 Cat
,只需要满足 Animal
兼容 Cat
或 Cat
兼容 Animal
即可animal
赋值给 tom
,需要满足 Cat
兼容 Animal
才行但是 Cat
并不兼容 Animal
。
End~
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。