- 类型断言(Type Assertion)
- 类型断言 👉 指鹿为马
- 「欺骗」ts,相当于告诉 ts“我知道我正在做什么,你可以信任我”
- 类型断言只会影响 ts 编译时的类型,类型断言语句在编译结果中会被删除,并不会对编译结果产生任何影响
- 类型断言语法
值 as 类型
(推荐)<类型>值
(不推荐)
- 为某些变量指定一个更具体的类型,从而能够访问该类型的属性或方法,即使编译器原本不认为它是那个类型
- 类型断言可以让一个变量的类型更加明确,或者更加模糊
- 明确:一个变量可能是多个类型,将其断言为指定的类型,让 ts 明确知道它是什么类型
- 模糊:一个变量的类型是明确的,将其断言为 any 任意类型
function foo(): number | string {
if (Math.random() < 0.5) return 123
else return 'abc'
}
let bar = foo(); // bar 可能是 number 可能是 string
// ok
(bar as string).length; // 断言 bar 是 string
// ok
(bar as number).toString().length; // 断言 bar 是 number
(bar as string).length;
当程序执行到这里时,实际上我们并不知道 bar 的类型具体是什么。对于类似这样无法明确变量的具体类型的场景,我们都可以使用类型断言,来明确告诉 ts,你不知道它是什么类型没关系,我告诉你一个类型,你把它视作这个类型来处理就好(即便我告诉你的类型是错误的,你将错就错即可)。比如,此时如果我们想要让 ts 认为 bar 是 string 类型,可以这么写 bar as string
。
interface ApiError extends Error {
code: number;
}
interface HttpError extends Error {
statusCode: number;
}
class CustomError implements Error {
name: string;
message: string;
constructor(message: string) {
this.name = "CustomError";
this.message = message;
}
}
class ApiErrorClass extends CustomError implements ApiError {
code: number;
constructor(message: string, code: number) {
super(message);
this.code = code;
}
}
class HttpErrorClass extends CustomError implements HttpError {
statusCode: number;
constructor(message: string, statusCode: number) {
super(message);
this.statusCode = statusCode;
}
}
function isApiError(error: Error) {
if (typeof (error as ApiError).code === "number") {
return true;
}
return false;
}
const apiErr = new ApiErrorClass("API error occurred.", 5001);
const httpErr = new HttpErrorClass("HTTP error occurred.", 404);
console.log(isApiError(apiErr)); // true
console.log(isApiError(httpErr)); // false
在这个示例中,类型断言的作用主要是告诉 ts 编译器:”我知道我正在做什么,你可以信任我。”
function isApiError(error: Error) {
if (typeof (error as ApiError).code === "number") {
return true;
}
return false;
}
当你尝试访问 error.code
时,ts 会抱怨说 Error
接口上没有 code
属性。但你知道,可能传入的 error
对象是 ApiError
类型。为了绕过编译器的类型检查,并安全地访问 .code
属性,你使用了类型断言 (error as ApiError)
。
这样,你基本上是告诉 ts:“相信我,我知道这个 error
对象有一个 code
属性,你可以让我访问它。”。这就是类型断言的主要作用,它允许你为某些变量指定一个更具体的类型,从而能够访问该类型的属性或方法,即使编译器原本不认为它是那个类型。
// error
window.foo = 1
// ok
(window as any).foo = 1
- 类型断言约束
- ts 无法将一个类型断言为任意一个其它类型,断言是有限制的,要求类型之间必须“兼容”才能断言
- 毫无根据的断言是非常危险的,要使得 A 能够被断言为 B,只需要
A > B
或B > A
即可
- 类型断言小结
- 联合类型可以被断言为其中一个类型
- 父类可以被断言为子类
- 任何类型都可以被断言为 any
- any 可以被断言为任何类型
- 双重断言:
- 使用双重断言,可以将一个类型断言为任意一个其它类型
- 将数字类型 a 断言为字符串类型:
a as any as string
a as unknown as string
- 除非迫不得已,千万别用双重断言
let a1: number
let a2: number
let a3: number
// error 类型 "number" 到类型 "string" 的转换可能是错误的,因为两种类型不能充分重叠。
a1 as string
// ok
a2 as any as string
// ok
a3 as unknown as string
- 类型声明比类型断言更加严格
- 类型断言:
A as B
要求A > B
或者B > A
- 类型声明:
x1: A = x2
要求x2的类型 > A
interface Animal {
name: string;
}
interface Cat {
name: string;
run(): void;
}
const cat: Cat = {
name: "cat",
run() {
console.log("cat run");
},
};
const animal: Animal = {
name: "a",
};
// ok
let a1 = animal as Cat;
// error
let a2: Cat = animal;
// ok
let a3: Cat = cat;