繁体   English   中英

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

[英]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元组,其值恰好是按该顺序排列的数字85 具体来说,编译器知道args.length正好是2

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


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

Playground 代码链接


* 这对于数组和元组类型并不严格; 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 断言的其他相关问题/答案的列表:

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

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

另一方面,现在将as const添加到声明中确实使它成为常量。 整个东西是只读的,所以你根本不能修改数组。


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

“真的让它保持不变”可能意味着当没有运行时效果时会有一些运行时效果。 在运行时,args.push(30) 仍然会修改数组。 正如 const 所做的那样,如果 TypeScript 编译器看到你这样做,它就会抱怨。 – 杰卡尔兹

as const只影响编译器,它的只读效果有一个例外(见注释)。 但总的来说,这仍然是constas 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.

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