- 类型断言(Type Assertion)
- 类型断言 👉 指鹿为马
- 「欺骗」ts,相当于告诉 ts“我知道我正在做什么,你可以信任我”
- 类型断言只会影响 ts 编译时的类型,类型断言语句在编译结果中会被删除,并不会对编译结果产生任何影响
- 类型断言语法
值 as 类型(推荐)<类型>值(不推荐)
- 为某些变量指定一个更具体的类型,从而能够访问该类型的属性或方法,即使编译器原本不认为它是那个类型
- 类型断言可以让一个变量的类型更加明确,或者更加模糊
- 明确:一个变量可能是多个类型,将其断言为指定的类型,让 ts 明确知道它是什么类型
- 模糊:一个变量的类型是明确的,将其断言为 any 任意类型
function foo(): number | string {if (Math.random() < 0.5) return 123else 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)); // trueconsole.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 属性,你可以让我访问它。”。这就是类型断言的主要作用,它允许你为某些变量指定一个更具体的类型,从而能够访问该类型的属性或方法,即使编译器原本不认为它是那个类型。
// errorwindow.foo = 1// ok(window as any).foo = 1
- 类型断言约束
- ts 无法将一个类型断言为任意一个其它类型,断言是有限制的,要求类型之间必须“兼容”才能断言
- 毫无根据的断言是非常危险的,要使得 A 能够被断言为 B,只需要
A > B或B > A即可
- 类型断言小结
- 联合类型可以被断言为其中一个类型
- 父类可以被断言为子类
- 任何类型都可以被断言为 any
- any 可以被断言为任何类型
- 双重断言:
- 使用双重断言,可以将一个类型断言为任意一个其它类型
- 将数字类型 a 断言为字符串类型:
a as any as stringa as unknown as string
- 除非迫不得已,千万别用双重断言
let a1: numberlet a2: numberlet a3: number// error 类型 "number" 到类型 "string" 的转换可能是错误的,因为两种类型不能充分重叠。a1 as string// oka2 as any as string// oka3 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",};// oklet a1 = animal as Cat;// errorlet a2: Cat = animal;// oklet a3: Cat = cat;
