原始对象和扩展对象之间的 TypeScript 区别

TypeScript difference between original object and spreaded object

有个打字谜语我看不懂:

const  randomFn = (arg: Record<string, unknown>): string => 'kappa'

export type Values = {
    key: string;
};
const values: Values = {
    key: 'kappa'
}

const { ...spread } = values;

randomFn(values)
randomFn(spread) // produce Index signature for type 'string' is missing in type '{ transaction: string; }'.(2345)

为什么 Typescript 会为传播对象产生错误,而据我所知,它的输入应该是相同的。 如果我们检查 playgroud 中的对象,它与 TS 编译器完全相同。原创和传播。

TS 版本 4.5.4。 在 Type Script Playground

中复制

编辑:还进行双重传播以使其再次工作

randomFn({...spread}) // NO error

传播丢失类型,因为它不能保证传播对象的类型相同。考虑一下:

const { excluded, ...rest } = object;

显然rest不能和object是同一类型;缺少一把钥匙!

你也可以这样想:

const [excluded, ...rest] = array;

这就是为什么 spread 不是类型 Values

这是 TypeScript 设计限制的结果,请参阅 microsoft/TypeScript#42021 for details. When you use a rest element in an object destructuring assignment, TypeScript unfortunately does not copy an index signature into the type of the new variable. Nor does it allow the variable to have an implicit index signature (although I'm not sure why)。

你的 Values 类型别名允许有一个隐式索引签名(如果你把它设为 interface 那么它也不会,见 microsoft/TypeScript#15300),所以它是被认为可分配给 Record<string, unknown>(相当于 {[k: string]: unknown},一种具有字符串索引签名的类型)。但是 spread 作为 rest-destructured 变量不是,因此它失败了。

您注意到进行第二次剩余解构 确实 起作用,所以大概有一些关于如何标记对象类型以允许或禁止隐式索引签名的奇怪之处。这也是 microsoft/TypeScript#42021 中的 noted,尽管没有解释。

所以很遗憾,目前还没有一个非常令人满意的答案,除非 microsoft/TypeScript#42021 中出现更多 activity。