[英]What does the "as const" mean in TypeScript and what is its use case?
我对as const
演员感到困惑。 我检查了一些文件和视频,但没有完全理解。
我关心的是下面代码中的as const
是什么意思,使用它有什么好处?
const args = [8, 5] as const;
const angle = Math.atan2(...args);
console.log(angle);
这称为const
断言。 const
断言告诉编译器推断出它可以为表达式推断出的最窄的*或最具体的类型。 如果您将其关闭,编译器将使用其默认类型推断行为,这可能会导致更广泛或更通用的类型。
请注意,它被称为“断言”而不是“强制转换”。 TypeScript 中一般要避免使用“演员”一词; 当人们说“cast”时,他们通常暗示可以在运行时观察到的某种效果,但是 TypeScript 的类型系统,包括类型断言和const
断言,完全从发出的 JavaScript 中删除。 因此,使用 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()
function 恰好需要两个数字 arguments。 但是编译器只知道args
是一个数字数组。 它完全忘记了有两个元素,因此编译器抱怨您在需要两个元素时使用“0 或更多” arguments 调用Math.atan2()
。
将其与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
元组,其值恰好是按该顺序排列的数字8
和5
。 具体来说,编译器知道args.length
正好是2
。
这足以让Math.atan2()
的下一行工作。 编译器知道Math.atan2(...args)
与Math.atan2(8, 5)
相同,这是一个有效的调用。
再说一遍:在运行时,没有任何区别。 两个版本都将1.0121970114513341
记录到控制台。 但是const
断言,如 static 类型系统的 rest,并不意味着在运行时产生影响。 相反,它们让编译器更多地了解代码的意图,并且可以更准确地分辨出正确代码和错误之间的区别。
* 这对于数组和元组类型并不严格; 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]
,这是因为第一个声明等效于readonly tuple 。
断言为“完全只读”有一些例外,您可以在此处检查它们。 但是,一般好处是添加到其所有 object 属性的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 断言的其他相关问题/答案的列表:
如果您要编写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
之间的主要使用区别。 一个用于使引用不可变,另一个用于使引用的内容不可变。
那是一个const
断言。 这是关于它们的一个方便的帖子, 这里是文档。
当我们用 const 断言构造新的文字表达式时,我们可以向语言发出信号:
- 不应该扩大该表达式中的文字类型(例如,不要从“hello”变为字符串)
- object 文字获得只读属性
- 数组文字变成只读元组
使用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];
as const
应用于 object 或数组时,它使它们不可变(即使它们只读)。 对于其他文字,它可以防止类型扩大。
const args = [8, 5] as const;
args[0] = 10; ❌ Cannot assign to '0' because it is a read-only property.
其他几个优点:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.