简体   繁体   English

TypeScript 中的“as const”是什么意思,它的用例是什么?

[英]What does the "as const" mean in TypeScript and what is its use case?

I am confused about the as const cast.我对as const演员感到困惑。 I checked a few documents and videos but did not understand it fully.我检查了一些文件和视频,但没有完全理解。

My concern is what does the as const mean in the code below and what is the benefit of using it?我关心的是下面代码中的as const是什么意思,使用它有什么好处?

const args = [8, 5] as const;
const angle = Math.atan2(...args);
console.log(angle);

This is known as a const assertion .这称为const断言 A const assertion tells the compiler to infer the narrowest * or most specific type it can for an expression. const断言告诉编译器推断出它可以为表达式推断出的最窄*最具体的类型。 If you leave it off, the compiler will use its default type inference behavior, which will possibly result in a wider or more general type.如果您将其关闭,编译器将使用其默认类型推断行为,这可能会导致更广泛更通用的类型。

Note that it is called an "assertion" and not a "cast".请注意,它被称为“断言”而不是“强制转换”。 The term "cast" is generally to be avoided in TypeScript; TypeScript 中一般要避免使用“演员”一词; when people say "cast" they often imply some sort of effect that can be observed at runtime, but TypeScript's type system, including type assertions and const assertions, is completely erased from the emitted JavaScript.当人们说“cast”时,他们通常暗示可以在运行时观察到的某种效果,但是 TypeScript 的类型系统,包括类型断言和const断言,完全从发出的 JavaScript 中删除 So there is absolutely no difference at runtime between a program that uses as const and one that does not.因此,使用 as const 的程序和不使用as const的程序在运行时绝对没有区别。


At compile time, though, there is a noticeable difference.但是,在编译时,存在明显的差异。 Let's see what happens when you leave out as const in the above example:让我们看看在上面的示例中省略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);

The compiler sees const args = [8, 5];编译器看到const args = [8, 5]; and infers the type of number[] .并推断number[]的类型。 That's a mutable array of zero or more elements of type number .这是一个由零个或多个number类型元素组成的可变数组。 The compiler has no idea how many or which elements there are.编译器不知道有多少哪些元素。 Such an inference is generally reasonable;这样的推论通常是合理的; often, array contents are meant to be modified in some way.通常,数组内容意味着要以某种方式进行修改。 If someone wants to write args.push(17) or args[0]++ , they'll be happy with a type of number[] .如果有人想写args.push(17)args[0]++ ,他们会对number[]类型感到满意。

Unfortunately the next line, Math.atan2(...args) , results in an error.不幸的是,下一行Math.atan2(...args)导致错误。 The Math.atan2() function requires exactly two numeric arguments. Math.atan2() function 恰好需要两个数字 arguments。 But all the compiler knows about args is that it's an array of numbers.但是编译器只知道args是一个数字数组。 It has completely forgotten that there are two elements, and so the compiler complains that you are calling Math.atan2() with "0 or more" arguments when it wants exactly two.它完全忘记了有两个元素,因此编译器抱怨您在需要两个元素时使用“0 或更多” arguments 调用Math.atan2()


Compare that to the code with as const :将其与as const的代码进行比较:

const args = [8, 5] as const;
// const args: readonly [8, 5]
const angle = Math.atan2(...args); // okay
console.log(angle);

Now the compiler infers that args is of type readonly [8, 5] ... a readonly tuple whose values are exactly the numbers 8 and 5 in that order.现在编译器推断args的类型为readonly [8, 5] ... 一个readonly元组,其值恰好是按该顺序排列的数字85 Specifically, args.length is known to be exactly 2 by the compiler.具体来说,编译器知道args.length正好是2

And this is enough for the next line with Math.atan2() to work.这足以让Math.atan2()的下一行工作。 The compiler knows that Math.atan2(...args) is the same as Math.atan2(8, 5) , which is a valid call.编译器知道Math.atan2(...args)Math.atan2(8, 5)相同,这是一个有效的调用。


And again: at runtime, there is no difference whatsoever.再说一遍:在运行时,没有任何区别。 Both versions log 1.0121970114513341 to the console.两个版本都将1.0121970114513341记录到控制台。 But const assertions, like the rest of the static type system, are not meant to have effects at runtime.但是const断言,如 static 类型系统的 rest,并不意味着在运行时产生影响。 Instead, they let the compiler know more about the intent of the code, and can more accurately tell the difference between correct code and bugs.相反,它们让编译器更多地了解代码的意图,并且可以更准确地分辨出正确代码和错误之间的区别。

Playground link to code Playground 代码链接


* This isn't strictly true for array and tuple types; * 这对于数组和元组类型并不严格; a readonly array or tuple is technically wider than a mutable version. readonly数组或元组在技术上比可变版本更广泛 A mutable array is considered a subtype of a readonly array;可变数组被认为是readonly数组的子类型; the former is not known to have mutation methods like push() while the latter does.前者不知道有像push()这样的变异方法,而后者有。

In short words, it lets you create fully readonly objects , this is known as const assertion , at your code as const means that the array positions values are readonly , here's an example of how it works:简而言之,它允许您创建完全只读的对象,这被称为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]'"

You can see at the last thrown error, that args = [8, 5] as const is interpreted as args: readonly [8, 5] , that's because the first declaration is equivalent to a readonly tuple .您可以在最后抛出的错误中看到args = [8, 5] as const被解释为args: readonly [8, 5] ,这是因为第一个声明等效于readonly tuple

There's a few exceptions for the asserts being 'fully readonly', you can check them here .断言为“完全只读”有一些例外,您可以在此处检查它们。 However, the general benefit is the readonly behaviour that is added to all its object attributes .但是,一般好处是添加到其所有 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

For more details, here's a list of other related question/answers that helped me understand the const assertion:有关更多详细信息,以下是帮助我理解 const 断言的其他相关问题/答案的列表:

  1. How do I declare a read-only array tuple in TypeScript? 如何在 TypeScript 中声明一个只读数组元组?
  2. Any difference between type assertions and the newer as operator in TypeScript? TypeScript 中的类型断言和较新as运算符之间有什么区别?
  3. Typescript readonly tuples Typescript 只读元组

If you were to write const args = [8, 5] , nothing would prevent you from then also writing args[0] = 23 or args.push(30) or anything else to modify that array.如果您要编写const args = [8, 5] ,那么没有什么可以阻止您同时编写args[0] = 23args.push(30)或其他任何东西来修改该数组。 All you've done is tell TS/JS that the variable named args points to that specific array, so you can't change what it's referencing (eg you can't do args = "something else" ).您所做的只是告诉 TS/JS 名为args的变量指向该特定数组,因此您无法更改它所引用的内容(例如,您无法执行args = "something else" )。 You can modify the array, you just can't change what its variable is pointing to.您可以修改数组,但不能更改其变量指向的内容。

On the other hand, adding as const to a declaration now really makes it constant.另一方面,现在将as const添加到声明中确实使它成为常量。 The whole thing is read-only, so you can't modify the array at all.整个东西是只读的,所以你根本不能修改数组。


To clarify, as pointed out in the comments:为了澄清,正如评论中指出的那样:

"really makes it constant" could imply that there is some runtime effect when there is none. “真的让它保持不变”可能意味着当没有运行时效果时会有一些运行时效果。 At runtime, args.push(30) will still modify the array.在运行时,args.push(30) 仍然会修改数组。 All as const does is make it so that the TypeScript compiler will complain if it sees you doing it.正如 const 所做的那样,如果 TypeScript 编译器看到你这样做,它就会抱怨。 – jcalz – 杰卡尔兹

as const only affects the compiler, and there is an exception to its read-only effect (see the comments). as const只影响编译器,它的只读效果有一个例外(见注释)。 But in general, that's still the major use difference between const and as const .但总的来说,这仍然是constas const之间的主要使用区别。 One is used to make a reference immutable and the other is used to make what's being referenced immutable.一个用于使引用不可变,另一个用于使引用的内容不可变。

That is a const assertion.那是一个const断言。 Here is a handy post on them , and here is the documentation . 这是关于它们的一个方便的帖子这里是文档

When we construct new literal expressions with const assertions, we can signal to the language that当我们用 const 断言构造新的文字表达式时,我们可以向语言发出信号:

  • no literal types in that expression should be widened (eg no going from "hello" to string)不应该扩大该表达式中的文字类型(例如,不要从“hello”变为字符串)
  • object literals get readonly properties object 文字获得只读属性
  • array literals become readonly tuples数组文字变成只读元组

With const args = [8, 5] as const;使用const args = [8, 5] as const; , the third bullet applies, and tsc will understand it to mean: ,第三个项目符号适用,并且 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];

Without the assertion:没有断言:

// Type: number[]
const args = [8, 5];

// Ok
args[0];
args[1];

// Also Ok.
args[2];

as const when applied to an object or array it makes them immutable (ie making them read-only). as const应用于 object 或数组时,它使它们不可变(即使它们只读)。 For other literals it prevents type widening.对于其他文字,它可以防止类型扩大。

const args = [8, 5] as const;

args[0] = 10; ❌ Cannot assign to '0' because it is a read-only property.

Few other advantages :其他几个优点

  • You can catch bugs at compile-time without running the program if you don't cast to a different type.如果您不强制转换为其他类型,您可以在编译时捕获错误而无需运行程序。
  • The compiler will not allow you to reassign properties of nested objects.编译器不允许您重新分配嵌套对象的属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM