TS 类型断言(下)

371 阅读1分钟
  • 类型断言的限制

并不是任何一个类型都可以被断言为任何另一个类型。

具体来说,若 A 兼容 B,那么 A 能够被断言为 B,B 也能被断言为 A。

下面我们通过一个简化的例子,来理解类型断言的限制:

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

let tom: Cat = {
    name: 'Tom',
    run: () => { console.log('run') }
};
let animal: Animal = tom;

在上面的例子中,Cat 包含了 Animal 中的所有属性,除此之外,它还有一个额外的方法 run。

TypeScript 只会看它们最终的结构有什么关系——所以它与 Cat extends Animal 是等价的:

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

那么也不难理解为什么 Cat 类型的 tom 可以赋值给 Animal 类型的 animal 了——就像面向对象编程中我们可以将子类的实例赋值给类型为父类的变量。

我们把它换成 TypeScript 中更专业的说法,即:Animal 兼容 Cat。

当 Animal 兼容 Cat 时,它们就可以互相进行类型断言了

  • 双重断言

    • 任何类型都可以被断言为 any
    • any 可以被断言为任何类型

    那么我们是不是可以使用双重断言 as any as Foo 来将任何一个类型断言为任何另一个类型呢? 可以,但永远不要这么使用。

  • 类型断言 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');

tom.run();
  • 类型断言 vs 泛型

// 使用泛型是解决上面那个例子的最好方案

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();