TypeScript [隔离声明] 添加一个始终作为计算属性名称发出的计算属性名称的语法形式,

8hhllhi2  于 6个月前  发布在  TypeScript
关注(0)|答案(5)|浏览(58)

🔍搜索词

isolatedDeclarations transpileModule computed property name

✅可行性检查清单

⭐建议

背景

目前,在isolatedDeclarations下的计算属性名称非常有限。今天,你可以写{[Symbol.iterator]: ...},仅此而已。这个限制之所以存在,是因为对于任意的{[expression]: ...},我们不知道该类型应该是{[expression]: something}{[expression: string]: something}甚至是{}(或者是未来的{f1: something} | {f2: something})。如今,类型中的计算属性名称必须完全是单个可延迟绑定的名称——不多也不少——与此同时,对象表达式(以及类声明)中的计算属性名称则更加灵活,允许我们使用更多内容。
到目前为止,这对TS用户来说效果很好,因为我们基本上预先解决并缓存了表达式计算名称解析为我们的声明文件的内容。不幸的是,对于isolatedDeclarations用户来说,这会带来问题,因为计算属性名称中的expression可能来自或依赖于另一个文件的类型信息。在这种情况下,我们无法知道如何发出表达式的类型。你可能会乐观地发出{[expression]: something},但如果expression最终在全程序上下文中评估为stringany,那么声明文件将产生错误并提供不正确的类型信息。

建议

在这种情况下,我们可以使用一种语义上的opt-in来确保在计算表达式的类型中保留计算属性名称。一种形式的计算属性名称意味着,当你看到它时,它总是确保计算属性名称出现在输出中,并且如果在检查时无法生成有效的计算属性名称的声明文件,就会发出检查器错误。
我建议我们重用一些现有的语法,并为其赋予新的意义来实现这一点——一个 satisfies keyof 后缀Assert,仅在计算属性名称位置有效,并且仅适用于点实体名称表达式。这意味着你可以写

export const a = {
  [something satisfies keyof]: () => {}
}

然后我们总是会发出

export const a: {
  [something]: () => void;
};

并且如果 something satisfies keyof 不是一个确切的单一唯一符号、字符串字面量或数字字面量类型(这是在类型位置计算属性名称中有效的),就会发出错误。

兼容性

只有 isolatedDeclarations 相关的作者真正需要考虑这个特性——它会被从声明文件中擦除,因为它们已经检查了这个约束,所以库消费者永远不会看到它。不使用 isolatedDeclarations 的人永远不会被迫使用它,因为他们总是可以在没有Assert的情况下生成声明类型。这很容易集成到 isolatedDeclarations 快速修复程序中。这与现有的 satisfies keyof T Assert不冲突,因为它们需要一个类型参数作为 keyof 的输入。将来还有可能允许在其他位置和对任意表达式种类上检查相同的不变式——即表达式必须完全是一个字面键类型——如果我们认为这样的检查在比计算属性名称更广泛的上下文中有用途的话。

附录:使错误情况更好

一旦有了 {[expression satsifies keyof]: ...} 的支持,我们就知道计算属性名称应该始终产生恰好一个对象键,即使 expression不会产生有效的键类型(因此会产生错误)。在这种情况下,可以通过覆盖 expression 的类型为特定于 expression 符号的属性键来提高其价值,然后在后续的 obj[expression] 查找失败时回退到使用该符号。这样一来,我们就可以尽可能地保留用户的意图,而不必迅速回归到未经检查的状态。这个方法很酷(我已经有一个可行的原型),特别是在我们的语言服务在后台加载整个程序时执行单文件检查模式的情况下,但实际上并不一定需要这个特性。我对这个问题的开放问题只是

  1. 支持这种场景是否值得用一个特殊情况来支持?
  2. 如果包含其中一个回退属性键的结果类型的值是 keyof 呢?应该调整 string | number | symbol 结果吗?回退错误属性应该完全从 keyof 中过滤掉吗?
svgewumm

svgewumm1#

...并在满足 keyof 条件的情况下对某些内容发出错误,如果某些内容不完全是单一的唯一符号、字符串字面量或数字字面量类型(这是在计算属性名时有效的类型位置)。

我想不明白的是,如果你已经能够对 something 位置中的任意表达式进行这种检查,那么额外的语法为你带来了什么好处?那时感觉就像是噪音。

kse8i1jr

kse8i1jr2#

我不明白的是,如果你已经能够对任意表达式进行检查,那么额外的语法能给你带来什么好处呢?这感觉就像是噪音。

给定

// @filename: node_modules/mod/index.ts
export const something = Math.random() ? 1 : 2;

,

import {something} from "mod";
export const a = {[something]: 1};

没有错误,而

import {something} from "mod";
export const a = {[something satisfies keyof]: 1};

something 上报错: A satisfies keyof computed property name must be exactly a single string, number, or unique symbol literal type
并且

import {something} from "mod";
export const a = {[something]: 1};

根据解析出的 something 的类型发出

export const a: {[something: number]: number};

,但是

import {something} from "mod";
export const a = {[something satisfies keyof]: 1};

总是发出,即使 something 无法解析:

import {something} from "mod";
export const a: {[something]: number};
hujrc8aj

hujrc8aj3#

这基本上是一种将声明文件中计算属性名称保留到输入文件中的错误提取方法。

pqwbnv8z

pqwbnv8z4#

啊,所以它基本上是一个语法提示,不要将其扩展为索引签名,我们总是希望它在类型中显示为计算属性。satisfies keyof 中的提示感觉非常尴尬和不直观,但这完全是在🚲 🏠的领域,可能还太早了。

r6hnlfcb

r6hnlfcb5#

既然Symbol.iterator在这里被提到了,我想把Symbol.asyncIterator也放进讨论中,因为我在其他地方没有看到它的身影,而且目前还会产生错误。

相关问题