这称为const assertion。 const 断言告诉编译器推断它可以为表达式推断出 最窄* 或 最具体 类型。如果您不使用它,编译器将使用其默认的类型推断行为,这可能会导致 更宽 或 更通用 类型。
请注意,它被称为“断言”而不是“强制转换”。在 TypeScript 中通常要避免使用“cast”一词;当人们说“cast”时,他们通常暗示某种可以在运行时观察到的效果,但是 TypeScript 的类型系统,包括类型断言和const 断言,完全是来自发出的 JavaScript 的erased。因此,使用as const 的程序和不使用as const 的程序之间在运行时绝对没有区别。
但是,在编译时,有一个明显的区别。让我们看看在上面的示例中省略 as const 会发生什么:
const args = [8, 5];
// const args: number[]
const angle = Math.atan2(...args); // error! Expected 2 arguments, but got 0 or more.
console.log(angle);
编译器看到const args = [8, 5]; 并推断number[] 的类型。这是一个由零个或多个number 类型元素组成的可变数组。编译器不知道有多少或有哪些元素。这样的推论通常是合理的;通常,数组内容意味着要以某种方式进行修改。如果有人想写args.push(17) 或args[0]++,他们会对number[] 的类型感到满意。
不幸的是,下一行 Math.atan2(...args) 导致错误。 Math.atan2() 函数需要两个数字参数。但是编译器所知道的关于args 的只是它是一个数字数组。它完全忘记了有两个元素,因此编译器会抱怨您在调用 Math.atan2() 时使用“0 或更多”参数,而它正好需要两个。
将其与 as const 的代码进行比较:
const args = [8, 5] as const;
// const args: readonly [8, 5]
const angle = Math.atan2(...args); // okay
console.log(angle);
现在编译器推断args 的类型为readonly [8, 5]... 一个readonly tuple,其值恰好是数字8 和5 按该顺序排列。具体来说,编译器知道args.length 就是2。
这足以让Math.atan2() 的下一行工作。编译器知道Math.atan2(...args) 与Math.atan2(8, 5) 相同,这是一个有效的调用。
再说一遍:在运行时,没有任何区别。两个版本都将1.0121970114513341 记录到控制台。但是const 断言与静态类型系统的其余部分一样,并不意味着在运行时产生影响。相反,它们让编译器更多地了解代码的意图,并且可以更准确地区分正确代码和错误之间的区别。
Playground link to code
* 这对于数组和元组类型并不严格; readonly 数组或元组在技术上比可变版本更宽。可变数组被认为是readonly 数组的子类型;前者不知道有像push() 这样的变异方法,而后者有。