我对 as const
演员感到困惑。我检查了一些文件和视频,但没有完全理解。
我关心的是下面代码中的 as const
是什么意思,使用它有什么好处?
const args = [8, 5] as const;
const angle = Math.atan2(...args);
console.log(angle);
1
总是 one (1)
一样,数字是常数,因为它们不能更改。
这称为 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
断言与静态类型系统的其余部分一样,并不意味着在运行时产生影响。相反,它们让编译器更多地了解代码的intent,并且可以更准确地分辨出正确代码和错误之间的区别。
* 这对于数组和元组类型并不严格; readonly
数组或元组在技术上比可变版本更宽。可变数组被视为 readonly
数组的子类型;前者不知道有像 push()
这样的变异方法,而后者有。
简而言之,它可以让您创建完全只读的对象,这被称为 const assertion
,在您的代码中 as const
表示数组位置值为 readonly
,这是一个示例作品:
const args = [8, 5] as const;
args[0] = 3; // throws "Cannot assign to '0' because it is a read-only
args.push(3); // throws "Property 'push' does not exist on type 'readonly [8, 5]'"
您可以在最后抛出的错误中看到,args = [8, 5] as const
被解释为 args: readonly [8, 5]
,这是因为 the first declaration 等同于 readonly tuple。
断言是“完全只读的”有一些例外,您可以检查它们here。但是,一般的好处是 添加到其所有对象属性的 readonly
行为。
const args = [8, 5];
// Without `as const` assert; `args` stills a constant, but you can modify its attributes
args[0] = 3; // -- WORKS
args.push(3); // -- WORKS
// You are only prevented from assigning values directly to your variable
args = 7; // -- THROWS ERROR
有关更多详细信息,以下是帮助我理解 const 断言的其他相关问题/答案的列表:
如何在 TypeScript 中声明只读数组元组? TypeScript 中的类型断言和较新的 as 运算符之间有什么区别?打字稿只读元组
如果您要编写 const args = [8, 5]
,那么没有什么可以阻止您同时编写 args[0] = 23
或 args.push(30)
或其他任何东西来修改该数组。您所做的只是告诉 TS/JS 名为 args
的变量指向该特定数组,因此您无法更改它所引用的内容(例如,您不能执行 args = "something else"
)。您可以修改数组,但不能更改其变量指向的内容。
另一方面,现在将 as const
添加到声明中真的 使其保持不变。整个东西是只读的,所以你根本不能修改数组。
为了澄清,正如评论中指出的那样:
“真的让它保持不变”可能意味着当没有运行时效果时会有一些运行时效果。在运行时,args.push(30) 仍然会修改数组。正如 const 所做的那样,如果 TypeScript 编译器看到你这样做,它就会抱怨。 – 杰卡尔兹
as const
只影响编译器,它的只读效果有一个例外(见注释)。但总的来说,这仍然是 const
和 as const
之间的主要使用区别。一个用于使引用不可变,另一个用于使被引用的内容不可变。
as const
完全只读:请参阅 const assertions caveats。
args.push(30)
仍会修改数组。 as const
所做的只是让 TypeScript 编译器在看到你这样做时会抱怨。
这是一个 const
断言。 Here is a handy post on them 和 here is the documentation。
当我们用 const 断言构造新的文字表达式时,我们可以向语言发出信号,表示该表达式中的文字类型不应被扩展(例如,不要从“hello”变为字符串)对象文字获得只读属性数组文字变为只读元组
对于 const args = [8, 5] as const;
,第三个项目符号适用,并且 tsc 将其理解为:
// Type: readonly [8, 5]
const args = [8, 5] as const;
// Ok
args[0];
args[1];
// Error: Tuple type 'readonly [8, 5]' of length '2' has no element at index '2'.
args[2];
没有断言:
// Type: number[]
const args = [8, 5];
// Ok
args[0];
args[1];
// Also Ok.
args[2];
args['length']
)也可以访问“长度”属性。
length
是数组对象中的唯一属性。感谢您的快速回复。
as const
当应用于对象或数组时,它使它们不可变(即使它们只读)。对于其他文字,它可以防止类型扩大。
const args = [8, 5] as const;
args[0] = 10; ❌ Cannot assign to '0' because it is a read-only property.
其他几个优点:
如果您不强制转换为其他类型,您可以在编译时捕获错误而无需运行程序。
编译器不允许您重新分配嵌套对象的属性。
60 * 60 * 1000
不是文字,它是计算出来的,有关此问题的更多详细信息,请参阅 the PR 介绍它们。 adding math 上存在文字类型的未解决问题