as
TypeScript 类型检测无法做到绝对智能,毕竟程序不能像人一样思考。有时会碰到我们比 TypeScript 更清楚实际类型的情况,比如下面的例子:
const arrayNumber: number[] = [1, 2, 3, 4];const greaterThan2: number = arrayNumber.find(num => num > 2);// 提示 ts(2322)// 不能将类型“number | undefined”分配给类型“number”。// 不能将类型“undefined”分配给类型“number”。
在 TypeScript 看来,greaterThan2 的类型既可能是数字,也可能是 undefined,所以上面的示例中提示了一个 ts(2322) 错误,此时我们不能把类型 undefined 分配给类型 number。
不过,我们可以使用一种笃定的方式——类型断言(类似仅作用在类型层面的强制类型转换)告诉 TypeScript 按照我们的方式做类型检查。
const arrayNumber: number[] = [1, 2, 3, 4];const greaterThan2: number = arrayNumber.find(num => num > 2) as number;
尖括号<>
或者用尖括号 + 类型的格式做类型断言
当你在TypeScript里使用JSX时,只有 as语法断言是被允许的。
const arrayNumber: number[] = [1, 2, 3, 4];const greaterThan2: number = <number>arrayNumber.find(num => num > 2);
感叹号!
此外还有一种特殊非空断言,即在值(变量、属性)的后边添加 ‘!’ 断言操作符,它可以用来排除值为 null、undefined 的情况,具体示例如下:
let mayNullOrUndefinedOrString: null | undefined | string;mayNullOrUndefinedOrString?.toString(); // okmayNullOrUndefinedOrString!.toString(); // okmayNullOrUndefinedOrString.toString(); // ts(2533) 对象可能为 "null" 或“未定义”。
const 断言
TypeScript 3.4 引入了一种新的字面量构造方式,也称为 const 断言。当我们使用 const 断言构造新的字面量表达式时,我们可以向编程语言发出以下信号:
- 表达式中的任何字面量类型都不应该被扩展;
- 对象字面量的属性,将使用 readonly 修饰;
- 数组字面量将变成 readonly 元组。
下面我们来举一个 const 断言的例子:
let x = "hello" as const;type X = typeof x; // type X = "hello"let y = [10, 20] as const;type Y = typeof y; // type Y = readonly [10, 20]let z = { text: "hello" } as const;type Z = typeof z; // let z: { readonly text: "hello"; }
数组字面量应用 const 断言后,它将变成 readonly 元组,之后我们还可以通过 typeof 操作符获取元组中元素值的联合类型,具体如下:
type Data = typeof y[number]; // type Data = 10 | 20
这同样适用于包含引用类型的数组,比如包含普通的对象的数组。这里我们也来举一个具体的例子:
const locales = [{locale: "zh-CN",language: "中文"},{locale: "en",language: "English"}] as const;// type Locale = "zh-CN" | "en"type Locale = typeof locales[number]["locale"];
另外在使用 const 断言的时候,我们还需要注意以下两个注意事项:
- 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;
2. const 上下文不会立即将表达式转换为完全不可变```typescriptlet arr = [1, 2, 3, 4];let foo = {name: "foo",contents: arr,} as const;foo.name = "bar"; // error!foo.contents = []; // error!foo.contents.push(5); // ...works!
双重断言
既然:
- 任何类型都可以被断言为 any
- any 可以被断言为任何类型
那么我们是不是可以使用双重断言 as any as Foo 来将任何一个类型断言为任何另一个类型呢?
interface Cat {run(): void;}interface Fish {swim(): void;}function testCat(cat: Cat) {return (cat as any as Fish);}
在上面的例子中,若直接使用 cat as Fish 肯定会报错,因为 Cat 和 Fish 互相都不兼容。
但是若使用双重断言,则可以打破「要使得 A 能够被断言为 B,只需要 A 兼容 B 或 B 兼容 A 即可」的限制,将任何一个类型断言为任何另一个类型。
若你使用了这种双重断言,那么十有八九是非常错误的,它很可能会导致运行时错误。
除非迫不得已,千万别用双重断言。
