Skip to content

TS清单 - 类型断言

类型断言

类型断言(Type Assertion)可以用来手动指定一个值的类型。
语法:值 as 类型;或 <类型>值

在 tsx 语法(React 的 jsx 语法的 ts 版)中必须使用前者,即 值 as 类型。建议统一使用 「值 as 类型」 这样的语法。

用途一:将一个联合类型断言为其中一个类型

注意

类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误,所以不要滥用。

js
// 以下代码编译时不会报错,但是运行时会报错
interface Cat {
    name: string;
    run(): void;
}
interface Fish {
    name: string;
    swim(): void;
}
function swim(animal: Cat | Fish) {
    (animal as Fish).swim();
}
const kity: Cat = {
    name: 'kity',
    run() {
        console.log('run')
    }
}
swim(kity)

用途二:将一个父类断言为更加具体的子类。

js
class ApiError extends Error {
    code: number = 0;
}
class HttpError extends Error {
    statusCode: number = 200;
}

function isApiError(error: Error) {
    if (typeof (error as ApiError).code === 'number') {
        return true;
    }
    // 当 ApiError和HttpError 是class类来实现继承的时候,可以使用「instanceof」来判断
    // if (error instanceof ApiError) {
    //     return true;
    // }
    return false;
}

但是如果 ApiError和HttpError 不是用class类来定义,而是用「interface」接口来定义的话,则不能使用「instanceof」来判断处理了。因为接口是一个类型,不是一个真正的值,它在编译结果中会被删除。此时只能用「类型断言」来处理。

用途三:将任何一个类型断言为 any

js
// 下面这行代码编译时会报错
window.foo = 1;     // 提示报错: 类型“Window & typeof globalThis”上不存在属性“foo”。

// 如果改成类型断言形式,则不会报错
(window as any).foo = 1;

将一个变量断言为 any 可以说是解决 TypeScript 中类型问题的最后一个手段。

它极有可能掩盖了真正的类型错误,所以如果不是非常确定,就不要使用 as any。

用途四:将 any 断言为一个具体的类型

js
// ====== 老旧代码 start ======
function getCacheData(key: string): any {
    return (window as any).cache[key];
}
// ====== 老旧代码  end  ======

interface Cat {
    name: string;
    run(): void;
}
// 我们在使用老旧代码的时候(如果不能直接修改其本身),但是我们可以将其返回值断言成更精确的类型,而不是any满天飞。
const tom = getCacheData('tom') as Cat;
tom.run();

类型断言的限制

html
1. 联合类型可以被断言为其中一个类型
2. 父类可以被断言为子类
3. 任何类型都可以被断言为 any
4. any 可以被断言为任何类型
5. 要使得 A 能够被断言为 B,只需要 A 兼容 B 或 B 兼容 A 即可
<!-- 1~4,都是5的特例 -->

双重断言

双重断言

除非迫不得已,千万别用双重断言。

js
interface Cat {
    run(): void;
}
interface Fish {
    swim(): void;
}
// 这种双重断言,那么十有八九是非常错误的,它很可能会导致运行时错误。
function testCat(cat: Cat) {
    return (cat as any as Fish);
}

类型断言 vs 类型声明

类型声明是比类型断言更加严格的。

js
// ====== 1. 类型断言(下面代码编译时OK)
interface Animal {
    name: string;
}
interface Cat {
    name: string;
    run(): void;
}

const animal: Animal = {
    name: 'tom'
};
let tom = animal as Cat;

// ====== 2. 类型声明(下面代码编译时报错)
interface Animal {
    name: string;
}
interface Cat {
    name: string;
    run(): void;
}

const animal: Animal = {
    name: 'tom'
};
let tom: Cat = animal;          // 提示报错: 类型 "Animal" 中缺少属性 "run",但类型 "Cat" 中需要该属性。

类型断言 vs 泛型

js
// 接上面的例子(老旧代码),增加一个泛型 <T>,可以对 getCacheData 的返回值实现更规范的约束。
function getCacheData<T>(key: string): T {
    return (window as any).cache[key];
}

interface Cat {
    name: string;
    run(): void;
}

const tom = getCacheData<Cat>('tom');
tom.run();

参考地址: 类型断言