1. 类型断言(Type Assertion)
    2. 类型断言 👉 指鹿为马
      1. 「欺骗」ts,相当于告诉 ts“我知道我正在做什么,你可以信任我”
      2. 类型断言只会影响 ts 编译时的类型,类型断言语句在编译结果中会被删除,并不会对编译结果产生任何影响
    3. 类型断言语法
      1. 值 as 类型(推荐)
      2. <类型>值(不推荐)
    4. 为某些变量指定一个更具体的类型,从而能够访问该类型的属性或方法,即使编译器原本不认为它是那个类型
    5. 类型断言可以让一个变量的类型更加明确,或者更加模糊
      1. 明确:一个变量可能是多个类型,将其断言为指定的类型,让 ts 明确知道它是什么类型
      2. 模糊:一个变量的类型是明确的,将其断言为 any 任意类型
    1. function foo(): number | string {
    2. if (Math.random() < 0.5) return 123
    3. else return 'abc'
    4. }
    5. let bar = foo(); // bar 可能是 number 可能是 string
    6. // ok
    7. (bar as string).length; // 断言 bar 是 string
    8. // ok
    9. (bar as number).toString().length; // 断言 bar 是 number

    (bar as string).length; 当程序执行到这里时,实际上我们并不知道 bar 的类型具体是什么。对于类似这样无法明确变量的具体类型的场景,我们都可以使用类型断言,来明确告诉 ts,你不知道它是什么类型没关系,我告诉你一个类型,你把它视作这个类型来处理就好(即便我告诉你的类型是错误的,你将错就错即可)。比如,此时如果我们想要让 ts 认为 bar 是 string 类型,可以这么写 bar as string

    1. interface ApiError extends Error {
    2. code: number;
    3. }
    4. interface HttpError extends Error {
    5. statusCode: number;
    6. }
    7. class CustomError implements Error {
    8. name: string;
    9. message: string;
    10. constructor(message: string) {
    11. this.name = "CustomError";
    12. this.message = message;
    13. }
    14. }
    15. class ApiErrorClass extends CustomError implements ApiError {
    16. code: number;
    17. constructor(message: string, code: number) {
    18. super(message);
    19. this.code = code;
    20. }
    21. }
    22. class HttpErrorClass extends CustomError implements HttpError {
    23. statusCode: number;
    24. constructor(message: string, statusCode: number) {
    25. super(message);
    26. this.statusCode = statusCode;
    27. }
    28. }
    29. function isApiError(error: Error) {
    30. if (typeof (error as ApiError).code === "number") {
    31. return true;
    32. }
    33. return false;
    34. }
    35. const apiErr = new ApiErrorClass("API error occurred.", 5001);
    36. const httpErr = new HttpErrorClass("HTTP error occurred.", 404);
    37. console.log(isApiError(apiErr)); // true
    38. console.log(isApiError(httpErr)); // false

    在这个示例中,类型断言的作用主要是告诉 ts 编译器:”我知道我正在做什么,你可以信任我。”

    1. function isApiError(error: Error) {
    2. if (typeof (error as ApiError).code === "number") {
    3. return true;
    4. }
    5. return false;
    6. }

    当你尝试访问 error.code 时,ts 会抱怨说 Error 接口上没有 code 属性。但你知道,可能传入的 error 对象是 ApiError 类型。为了绕过编译器的类型检查,并安全地访问 .code 属性,你使用了类型断言 (error as ApiError)

    这样,你基本上是告诉 ts:“相信我,我知道这个 error 对象有一个 code 属性,你可以让我访问它。”。这就是类型断言的主要作用,它允许你为某些变量指定一个更具体的类型,从而能够访问该类型的属性或方法,即使编译器原本不认为它是那个类型。

    1. // error
    2. window.foo = 1
    3. // ok
    4. (window as any).foo = 1
    1. 类型断言约束
      1. ts 无法将一个类型断言为任意一个其它类型,断言是有限制的,要求类型之间必须“兼容”才能断言
      2. 毫无根据的断言是非常危险的,要使得 A 能够被断言为 B,只需要 A > BB > A 即可
    2. 类型断言小结
      1. 联合类型可以被断言为其中一个类型
      2. 父类可以被断言为子类
      3. 任何类型都可以被断言为 any
      4. any 可以被断言为任何类型
    3. 双重断言:
      1. 使用双重断言,可以将一个类型断言为任意一个其它类型
      2. 将数字类型 a 断言为字符串类型:
        1. a as any as string
        2. a as unknown as string
      3. 除非迫不得已,千万别用双重断言
    1. let a1: number
    2. let a2: number
    3. let a3: number
    4. // error 类型 "number" 到类型 "string" 的转换可能是错误的,因为两种类型不能充分重叠。
    5. a1 as string
    6. // ok
    7. a2 as any as string
    8. // ok
    9. a3 as unknown as string
    1. 类型声明比类型断言更加严格
    2. 类型断言:A as B 要求 A > B 或者 B > A
    3. 类型声明:x1: A = x2 要求 x2的类型 > A
    1. interface Animal {
    2. name: string;
    3. }
    4. interface Cat {
    5. name: string;
    6. run(): void;
    7. }
    8. const cat: Cat = {
    9. name: "cat",
    10. run() {
    11. console.log("cat run");
    12. },
    13. };
    14. const animal: Animal = {
    15. name: "a",
    16. };
    17. // ok
    18. let a1 = animal as Cat;
    19. // error
    20. let a2: Cat = animal;
    21. // ok
    22. let a3: Cat = cat;