- TypeScript 允许你覆盖它的推断,并且能以你任何你想要的方式分析它,这种机制被称为「类型断言」。
- TypeScript 类型断言用来告诉编译器你比它更了解这个类型,并且它不应该再发出错误。
// 这里的代码发出了错误警告,因为 foo 的类型推断为 {},即没有属性的对象。
// 因此,你不能在它的属性上添加 bar 或 bas.
const foo = {};
foo.bar = 123; // Error: 'bar' 属性不存在于 ‘{}’
foo.bas = 'hello'; // Error: 'bas' 属性不存在于 '{}'
interface Foo {
bar: number;
bas: string;
}
const foo = {} as Foo;
foo.bar = 123;
foo.bas = 'hello';
as foo
的语法来为类型断言,而不是
语法。// 最初的断言语法如下所示:
let foo: any;
let bar = <string>foo; // 现在 bar 的类型是 'string'
// 然而,当你在 JSX 中使用 的断言语法时,这会与 JSX 的语法存在歧义:
let foo = <string>bar;</string>;
function handler(event: Event) {
// Error: 'Event' 和 'HTMLElement' 中的任何一个都不能赋值给另外一个
const element = event as HTMLElement;
}
/*
如果你仍然想使用那个类型,你可以使用双重断言。
首先断言成兼容所有类型的 any,编译器将不会报错.
*/
function handler(event: Event) {
const element = (event as any) as HTMLElement; // ok
}
interface Cat {
name: string;
run(): void;
}
interface Fish {
name: string;
swim(): void;
}
/*
当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,
我们只能访问此联合类型的所有类型中共有的属性或方法!
*/
function getName(animal: Cat | Fish) {
return animal.name;
}
/*
我们需要在还不确定类型的时候就访问其中一个类型特有的属性或方法,比如
*/
function isFish(animal: Cat | Fish) {
// 获取 animal.swim 的时候会报错
if (typeof animal.swim === 'function') {
return true;
}
return false;
}
/*
此时可以使用类型断言,将 animal 断言成 Fish,从而解决报错
*/
function isFish(animal: Cat | Fish) {
if (typeof (animal as Fish).swim === 'function') {
return true;
}
return false;
}
TypeScript
编译器,无法避免运行时的错误interface Cat {
name: string;
run(): void;
}
interface Fish {
name: string;
swim(): void;
}
function swim(animal: Cat | Fish) {
// 这段代码隐藏了 animal 可能为 Cat 的情况,将 animal 直接断言为 Fish 了
(animal as Fish).swim();
}
/*
swim 函数接受的参数是 Cat | Fish,一旦传入的参数是 Cat 类型的变量,
由于 Cat 上没有 swim 方法,就会导致运行时错误了
*/
const tom: Cat = {
name: 'Tom',
run() { console.log('run') }
};
// 而 TypeScript 编译器信任了我们的断言,故在调用 swim() 时没有编译错误
swim(tom);
/*
该例子编译时不会报错,但在运行时会报错:
Uncaught 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) {
// 由于父类 Error 中没有 code 属性,故直接获取 error.code 会报错,
// 需要使用类型断言获取 (error as ApiError).code
if (typeof (error as ApiError).code === 'number') {
return true;
}
return false;
}
instanceof
不一定合适,虽然类能够通过 instanceof
来判断 error
是否是它的实例ApiError
和 HttpError
不是一个真正的类,而只是一个 TypeScript
的接口(interface
)interface ApiError extends Error {
code: number;
}
interface HttpError extends Error {
statusCode: number;
}
function isApiError(error: Error) {
// 会报错
if (error instanceof ApiError) {
return true;
}
return false;
}
window.foo = 1;
// index.ts:1:8 - error TS2339: Property 'foo' does not exist on type 'Window & typeof globalThis'.
window
上添加一个属性 foo
,并使他不报错// 此时我们可以使用 as any 临时将 window 断言为 any 类型
(window as any).foo = 1;
在日常的开发中,我们不可避免的需要处理 any 类型的变量,它们可能是由于第三方库未能定义好自己的类型,也有可能是历史遗留的或其他人编写的烂代码,还可能是受到 TypeScript 类型系统的限制而无法精确定义类型的场景。
getCacheData
,它的返回值是 any
function getCacheData(key: string | number): any {
return key;
}
function getCacheData(key: string | number): any {
return key;
}
getCacheData('tom') as string;
function toBoolean(something: any): boolean {
return something as boolean;
}
toBoolean(1);
// 返回值为 1
something
断言为 boolean
虽然可以通过编译,但是并没有什么用,代码在编译后会变成:function toBoolean(something) {
return something;
}
toBoolean(1);
// 返回值为 1
function toBoolean(something: any): boolean {
return Boolean(something);
}
toBoolean(1);
// 返回值为 true
tom
类型为 Cat
// 使用类型断言写法
function getCacheData(key: string): any {
return (window as any).cache[key];
}
interface Cat {
name: string;
run(): void;
}
const tom = getCacheData('tom') as Cat;
tom.run();
// 使用类型声明写法
function getCacheData(key: string): any {
return (window as any).cache[key];
}
interface Cat {
name: string;
run(): void;
}
const tom: Cat = getCacheData('tom');
tom.run();
interface Animal {
name: string;
}
interface Cat {
name: string;
run(): void;
}
const animal: Animal = {
name: 'tom'
};
// 使用类型断言写法
let tom = animal as Cat;
/*
分析:
animal 断言为 Cat,只需要满足 Animal 兼容 Cat 或 Cat 兼容 Animal 即可
由于 Animal 兼容 Cat,故可以将 animal 断言为 Cat 赋值给 tom
*/
// 使用类型声明写法
let tom: Cat = animal; // 这时会报错
/*
分析:
animal 赋值给 tom,需要满足 Cat 兼容 Animal 才行
这很容易理解,Animal 可以看作是 Cat 的父类,当然不能将父类的实例赋值给类型为子类的变量
*/
function getCacheData(key: string): any {
return (window as any).cache[key];
}
interface Cat {
name: string;
run(): void;
}
const tom = getCacheData('tom') as Cat;
tom.run();
/*
通过给 getCacheData 函数添加了一个泛型 ,
我们可以更加规范的实现对 getCacheData 返回值的约束,
这也同时去除掉了代码中的 any,是最优的一个解决方案。
*/
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();