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

Cih*_*man 26 type-assertion typescript

我对as const演员表感到困惑。我检查了一些文件和视频,但没有完全理解。

我担心的是as const下面代码中的含义是什么,使用它有什么好处?

const args = [8, 5] as const;
const angle = Math.atan2(...args);
console.log(angle);
Run Code Online (Sandbox Code Playgroud)

Lui*_*mas 75

简而言之,它可以让您创建完全只读的对象,这const assertion在您的代码中as const意味着数组位置值为readonly,这里是它如何工作的示例:

\n
const args = [8, 5] as const;\nargs[0] = 3;  // throws "Cannot assign to \'0\' because it is a read-only   \nargs.push(3); // throws "Property \'push\' does not exist on type \'readonly [8, 5]\'" \n
Run Code Online (Sandbox Code Playgroud)\n

您可以看到最后抛出的错误被args = [8, 5] as const解释为args: readonly [8, 5],这是因为第一个声明相当于一个只读元组

\n

元组有很多用例,一些例子是:扩展参数、破坏​​性值等。一般的好处是readonly添加到其所有对象属性的行为:

\n
const args = [8, 5];\n\n// Without `as const` assert; `args` stills a constant, but you can modify its attributes\nargs[0] = 3;  // -- WORKS \xe2\x9c\x93\nargs.push(3); // -- WORKS \xe2\x9c\x93\n\n// You are only prevented from assigning values directly to your variable\nargs = 7;     // -- THROWS ERROR \xe2\x9c\x96\n
Run Code Online (Sandbox Code Playgroud)\n

对于“完全只读”的断言,有一些例外情况,您可以在此处检查它们。有关更多详细信息,这里列出了其他相关问题/答案,帮助我理解 const 断言:

\n
    \n
  1. 如何在 TypeScript 中声明只读数组元组?
  2. \n
  3. TypeScript 中的类型断言和较新的as运算符之间有什么区别?
  4. \n
  5. Typescript 只读元组
  6. \n
\n


jca*_*alz 37

这称为const断言。甲const断言告诉编译器推断最窄*最具体它可以用于表达类型。如果您不使用它,编译器将使用其默认类型推断行为,这可能会导致更广泛更通用的类型。

请注意,它被称为“断言”而不是“强制转换”。在 TypeScript 中通常要避免使用术语“cast”;当人们说“强制转换”时,他们通常暗示某种可以在运行时观察到的效果,但 TypeScript 的类型系统,包括类型断言和const断言,已从发出的 JavaScript 中完全删除。因此,使用和不使用的程序在运行时绝对没有区别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);
Run Code Online (Sandbox Code Playgroud)

编译器看到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);
Run Code Online (Sandbox Code Playgroud)

现在编译器推断出args它的类型是readonly [8, 5]……一个readonly元组,它的值正好是数字,8并且是5按照这个顺序排列的。具体来说,args.length就是2编译器知道的。

这足以让下一行Math.atan2()工作。编译器知道这Math.atan2(...args)与 相同Math.atan2(8, 5),这是一个有效的调用。


再说一遍:在运行时,没有任何区别。两个版本都登录1.0121970114513341到控制台。但是const断言,与静态类型系统的其余部分一样,并不意味着在运行时产生影响。相反,它们让编译器更多地了解代码的意图,并且可以更准确地分辨正确代码和错误之间的区别。

Playground 链接到代码


* 对于数组和元组类型,严格来说并非如此;一个readonly阵列或元组在技术上是比可变版本。可变数组被认为是数组的子类型readonly;前者不像push()后者那样有变异方法是众所周知的。

  • 与 const 断言的结果相比,我不明白 `{readonly [k: string]: number}` 是如何“更好地键入”或“正确键入”的。在做出判断之前我需要看看“...”是什么。当您说“正确”或“更好”时,也许您的意思是“显式键入”或“注释”,但我不知道为什么显式键入比推理更受青睐。简而言之:‍♂️ (3认同)
  • @CihatŞaman - “‘const’断言只能应用于对枚举成员、字符串、数字、布尔值、数组或对象文字的引用。”。`60 * 60 * 1000` 不是文字,而是计算出来的,请参阅 [PR](https://github.com/Microsoft/TypeScript/pull/29510) 介绍它们以获取有关此事的更多详细信息。关于文字类型的[添加数学](https://github.com/microsoft/TypeScript/issues/26382) 存在一个未解决的问题 (2认同)
  • 与使用“as const”相比,正确输入它是否值得。考虑一下 `const args: Readonly<{ [key: string]: number}> = {...}` 的类型比 `const args = {...} as const` 更好,并且实现了相同的安全性。 (2认同)

use*_*737 36

如果您要编写const args = [8, 5],那么没有什么可以阻止您也编写args[0] = 23args.push(30)或其他任何内容来修改该数组。您所做的就是告诉 TS/JS 变量 nameargs指向该特定数组,因此您无法更改它引用的内容(例如,您不能这样做args = "something else")。您可以修改数组,但无法更改其变量指向的内容。

\n

另一方面,as const现在添加到声明中确实可以使其保持不变。整个数组是只读的,因此您根本无法修改数组。

\n
\n

为了澄清,正如评论中指出的:

\n
\n

“真正使其恒定”可能意味着在没有运行时效果的情况下存在一些运行时效果。在运行时,args.push(30) 仍然会修改数组。const 所做的一切都是为了让 TypeScript 编译器看到你这样做时会抱怨。\xe2\x80\x93 jcalz

\n
\n

as const只影响编译器,其只读效果有例外(见注释)。但总的来说,这仍然是const和之间的主要使用差异as const。一个用于使引用不可变,另一个用于使被引用的内容不可变。

\n

  • 不能代表投反对票的人说话。也许他们认为你的结束语句不明确(_“现在将 const 添加到声明中确实使其成为常量”_)?并非所有声明都是完全只读的 `as const`:请参阅 [const 断言警告](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#caveats-1)。 (2认同)

Roh*_*dal 16

as const当应用于对象或数组时,它使它们不可变(即使它们只读)。对于其他文字,它可以防止类型扩展。

\n
const args = [8, 5] as const;\n\nargs[0] = 10; \xe2\x9d\x8c Cannot assign to '0' because it is a read-only property.\n
Run Code Online (Sandbox Code Playgroud)\n

其他一些优点

\n
    \n
  • 如果不转换为不同的类型,您可以在编译时捕获错误,而无需运行程序。
  • \n
  • 编译器不允许您重新分配嵌套对象的属性。
  • \n
\n


ade*_*hox 8

已经添加了令人惊奇的答案,只是想删除另一个as const可能对某些人有用的示例,并且与其他答案中的示例有点不同:

\n
type Status = "success" | "danger"\n\nfunction foo(status: Status){\n  console.log({ status });\n}\n\nconst status = "success";\n\n// \xe2\x9d\x8c Error: Argument of type 'string' is not assignable to parameter of type 'Status'.\nfoo(status);\n
Run Code Online (Sandbox Code Playgroud)\n

它不起作用的原因是,当打字稿看到 时const status = "success",它会将状态变量的类型推断为字符串,而函数的状态参数的foo类型并不像“字符串”那么广泛,它更加具体,它要么有是“成功”还是“危险”。

\n

要解决此问题,您有两种方法:

\n
// #1: Call foo with "success" directly and TypeScript is smart enough to accept it:\nfoo("success");\n// #2: Solution #1 however is not always useable, e.g., when status is received  from\n// somewhere else, in that case, we have no other options but using a variable for it\n// That's how it can be done using a variable:\nfoo("success" as const);\n
Run Code Online (Sandbox Code Playgroud)\n

实际上,这与@jcalz 在他们的精彩回答中所说的没有什么不同:

\n
\n

const 断言告诉编译器为表达式推断最窄或最具体的类型。

\n
\n


Con*_*Low 6

那是一个const断言。这里有一篇关于它们的方便帖子这里是文档

当我们用 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];
Run Code Online (Sandbox Code Playgroud)

没有断言:

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

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

// Also Ok.
args[2];
Run Code Online (Sandbox Code Playgroud)