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

ippsafx7  于 2023-02-14  发布在  TypeScript
关注(0)|答案(5)|浏览(246)

我对as const演员阵容感到困惑,我检查了一些文档和视频,但没有完全理解。
我关心的是下面代码中的as const是什么意思,使用它有什么好处?

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

9njqaruj1#

这称为constAssert。constAssert告诉编译器为表达式推断 * 最窄**或 * 最具体 * 的类型。如果不设置它,编译器将使用其默认类型推断行为,这可能会导致 * 更宽 * 或 * 更一般 * 的类型。
请注意,它被称为"Assert"而不是"强制转换"。在TypeScript中通常要避免使用术语"强制转换";当人们说"cast"的时候,他们通常暗示着某种可以在运行时观察到的效果,但是TypeScript的类型系统,包括类型Assert和constAssert,从发出的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()函数需要两个数值参数,但是编译器只知道args是一个数字数组,它完全忘记了有两个元素,所以编译器抱怨你用"0或更多"参数调用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记录到控制台,但是constAssert,像静态类型系统的其他部分一样,并不意味着在运行时有效果,相反,它们让编译器更多地了解代码的 * 意图 *,并且可以更准确地区分正确代码和bug。
Playground代码链接

  • 对于数组和元组类型,这并不严格;readonly数组或元组在技术上比可变版本"更宽"。可变数组被认为是readonly数组的子类型;不知道前者具有如push()的突变方法,而后者具有。
mrfwxfqh

mrfwxfqh2#

简而言之,它可以让你创建完全只读的对象,这就是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],这是因为第一个声明等效于一个只读元组。

元组有很多用例,例如:spread parameters, destructive values, etc.。一般的好处是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

有一些Assert是“完全只读”的例外,你可以在这里查看。更多细节,这里有一个帮助我理解constAssert的其他相关问题/答案的列表:
1.如何在TypeScript中声明只读数组元组?

  1. Any difference between type assertions and the newer as operator in TypeScript?
  2. Typescript只读元组
icomxhvb

icomxhvb3#

如果你要写const args = [8, 5],没有什么能阻止你写args[0] = 23args.push(30)或其他任何东西来修改那个数组,你所做的就是告诉TS/JS变量args指向那个特定的数组,所以你不能改变它引用的东西(例如,你不能做args = "something else")。你可以修改数组,但不能改变它的变量指向什么。
另一方面,现在将as const添加到声明中 * 真的 * 会使它成为常量,整个声明都是只读的,所以根本不能修改数组。
澄清一下,正如评论中所指出的:
“really makes it constant”可能意味着在没有运行时效果的情况下存在一些运行时效果。在运行时,args.push(30)仍然会修改数组。const所做的一切就是使它成为一个常量,以便TypeScript编译器在看到你这样做时会发出抱怨。- jcalz
as const只影响编译器,它的只读效果有一个例外(见注解),但总的来说,这仍然是constas const在使用上的主要区别,一个用来使 * 一个引用 * 不可变,另一个用来使 * 被引用的内容 * 不可变。

wpcxdonn

wpcxdonn4#

这是一个constAssert,Here is a handy post on them,这里是文档。
当我们用constAssert构造新的文本表达式时,我们可以向语言发出信号

  • 表达式中的文字类型不应扩大(例如,不能从“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];

没有Assert:

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

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

// Also Ok.
args[2];
ygya80vv

ygya80vv5#

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

const args = [8, 5] as const;

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

其他优势很少**:**

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

相关问题