TS之类型断言

类型断言用于手动指定一个值的类型。

一、语法

值 as 类型

二、用途

2.1 将一个联合类型断言为其中一个类型

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

2.2 将一个父类断言为更加具体的子类

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判断似乎更为合适,如果ApiErrorHttpError为接口,那么就只能用断言来判断了:

interface ApiError extends Error{
    code:number;
}
interface HttpError extends Error{
    statusCode:number;
}

2.3 将任何一个类型断言为any

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,也不能完全否定它的作用,需要在类型的严格性和开发的便利性之间掌握平衡。

2.4 将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的访问时就有了代码补全,提高了代码的可维护性。

三、类型断言的限制

综上所述,类型断言有以下特点:

  1. 联合类型可以被断言为其中一个类型
  2. 父类可以被断言为子类
  3. 任何类型都可以被断言为any
  4. 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是结构类型系统,类型之间的对比只会比较它们最终的结构,而会忽略它们定义时的关系。所以在上例中,尽管AnimalCat定义时并没有任何关系,但结构上可以理解为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 是因为子类拥有父类的所有属性和方法,调用也不会出问题。

综上所述:

  1. 联合类型可以被断言为其中一个类型
  2. 父类可以被断言为子类
  3. 任何类型都可以被断言为any
  4. any可以被断言为任何类型
  5. 要使得A能够被断言为B,需要A兼容B或B兼容A

前四种情况都是最后一种的特例。

四、双重断言

既然:

  1. 任何类型都可以被断言为any
  2. any可以被断言为任何类型

那么类型岂不可以互相断言了?

const a:number = 666;
const b:string = a as any as string;

但这么做反而会导致一系列问题,所以,不到万不得已,最好不要这么做。

五、类型断言 VS 类型转换

类型断言只会影响TS编译时的类型,类型断言语句会在编译结果中删除。

function toBoolean(something: any): boolean {
    return something as boolean;
}
toBoolean(1);
// 返回值为 1
而类型转换则会影响编译结果:

function toBoolean(something: any): boolean {
    return Boolean(something);
}
toBoolean(1);
// 返回值为 true

六、类型断言 VS 类型声明

类型断言:

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。但直接申明tomCat类型是不可以的。

深入的讲,它们的核心区别就在于:

  • animal 断言为 Cat,只需要满足 Animal 兼容 CatCat 兼容 Animal 即可
  • animal 赋值给 tom,需要满足 Cat 兼容 Animal 才行

但是 Cat 并不兼容 Animal

  • 5
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值