as

TypeScript 类型检测无法做到绝对智能,毕竟程序不能像人一样思考。有时会碰到我们比 TypeScript 更清楚实际类型的情况,比如下面的例子:

  1. const arrayNumber: number[] = [1, 2, 3, 4];
  2. const greaterThan2: number = arrayNumber.find(num => num > 2);
  3. // 提示 ts(2322)
  4. // 不能将类型“number | undefined”分配给类型“number”。
  5. // 不能将类型“undefined”分配给类型“number”。

在 TypeScript 看来,greaterThan2 的类型既可能是数字,也可能是 undefined,所以上面的示例中提示了一个 ts(2322) 错误,此时我们不能把类型 undefined 分配给类型 number。

不过,我们可以使用一种笃定的方式——类型断言(类似仅作用在类型层面的强制类型转换)告诉 TypeScript 按照我们的方式做类型检查。

  1. const arrayNumber: number[] = [1, 2, 3, 4];
  2. const greaterThan2: number = arrayNumber.find(num => num > 2) as number;

尖括号<>

或者用尖括号 + 类型的格式做类型断言

当你在TypeScript里使用JSX时,只有 as语法断言是被允许的。

  1. const arrayNumber: number[] = [1, 2, 3, 4];
  2. const greaterThan2: number = <number>arrayNumber.find(num => num > 2);

感叹号!

此外还有一种特殊非空断言,即在值(变量、属性)的后边添加 ‘!’ 断言操作符,它可以用来排除值为 nullundefined 的情况,具体示例如下:

  1. let mayNullOrUndefinedOrString: null | undefined | string;
  2. mayNullOrUndefinedOrString?.toString(); // ok
  3. mayNullOrUndefinedOrString!.toString(); // ok
  4. mayNullOrUndefinedOrString.toString(); // ts(2533) 对象可能为 "null" 或“未定义”。

const 断言

TypeScript 3.4 引入了一种新的字面量构造方式,也称为 const 断言。当我们使用 const 断言构造新的字面量表达式时,我们可以向编程语言发出以下信号:

  • 表达式中的任何字面量类型都不应该被扩展;
  • 对象字面量的属性,将使用 readonly 修饰;
  • 数组字面量将变成 readonly 元组。

下面我们来举一个 const 断言的例子:

  1. let x = "hello" as const;
  2. type X = typeof x; // type X = "hello"
  3. let y = [10, 20] as const;
  4. type Y = typeof y; // type Y = readonly [10, 20]
  5. let z = { text: "hello" } as const;
  6. type Z = typeof z; // let z: { readonly text: "hello"; }

数组字面量应用 const 断言后,它将变成 readonly 元组,之后我们还可以通过 typeof 操作符获取元组中元素值的联合类型,具体如下:

  1. type Data = typeof y[number]; // type Data = 10 | 20

这同样适用于包含引用类型的数组,比如包含普通的对象的数组。这里我们也来举一个具体的例子:

  1. const locales = [
  2. {
  3. locale: "zh-CN",
  4. language: "中文"
  5. },
  6. {
  7. locale: "en",
  8. language: "English"
  9. }
  10. ] as const;
  11. // type Locale = "zh-CN" | "en"
  12. type Locale = typeof locales[number]["locale"];

另外在使用 const 断言的时候,我们还需要注意以下两个注意事项:

  1. const 断言只适用于简单的字面量表达式 ```typescript // A ‘const’ assertions can only be applied to references to enum members, // or string, number, boolean, array, or object literals. let a = (Math.random() < 0.5 ? 0 : 1) as const; // error

let b = Math.random() < 0.5 ? 0 as const : 1 as const;

  1. 2. const 上下文不会立即将表达式转换为完全不可变
  2. ```typescript
  3. let arr = [1, 2, 3, 4];
  4. let foo = {
  5. name: "foo",
  6. contents: arr,
  7. } as const;
  8. foo.name = "bar"; // error!
  9. foo.contents = []; // error!
  10. foo.contents.push(5); // ...works!

双重断言

既然:

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

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

  1. interface Cat {
  2. run(): void;
  3. }
  4. interface Fish {
  5. swim(): void;
  6. }
  7. function testCat(cat: Cat) {
  8. return (cat as any as Fish);
  9. }

在上面的例子中,若直接使用 cat as Fish 肯定会报错,因为 Cat 和 Fish 互相都不兼容。

但是若使用双重断言,则可以打破「要使得 A 能够被断言为 B,只需要 A 兼容 B 或 B 兼容 A 即可」的限制,将任何一个类型断言为任何另一个类型。

若你使用了这种双重断言,那么十有八九是非常错误的,它很可能会导致运行时错误。
除非迫不得已,千万别用双重断言。