参考资料: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 functionclass 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的访问时就有了代码补全,提高了代码的可维护性。
综上所述,类型断言有以下特点:
anyany可以被断言为任何类型但类型之间的断言却是有限制的。若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 是因为子类拥有父类的所有属性和方法,调用也不会出问题。综上所述:
anyany可以被断言为任何类型前四种情况都是最后一种的特例。
既然:
anyany可以被断言为任何类型那么类型岂不可以互相断言了?
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 删除。