首页 > 解决方案 > 将具有可选属性的类型强制为可索引类型

问题描述

尝试将具有可选属性的类型作为可索引类型传递时,Typescript 会引发错误:(Playground

type Thing = {
    thing1?: string
    thing2?: string
    thing3?: number
}

const thing: Thing = {}

function processObject (obj: { [key: string]: string | number }): string {
    /* Generic object handler, not specifically for Thing */
    return "test"
}

console.assert(processObject(thing) === "test")

这导致:

错误:'Thing' 类型的参数不能分配给'{ [key: string] 类型的参数:string | 数字; }'。属性“thing1”与索引签名不兼容。键入'字符串 | undefined' 不可分配给类型 'string | 数字'。类型 'undefined' 不能分配给类型 'string | 数字'。

当然,如果参数的类型被强制设为可选,它会起作用:

function processObject (obj: { [key: string]: string | number | undefined }): string {
    return "test"
}

不过,我不明白为什么这是必要的。根据TS PR #7029,可索引类型应该与具有相同隐式索引签名的其他类型兼容。

这些属性是否是可选的无关紧要-该属性根本不应该出现在对象上-对吗?为什么我必须指定undefined?有没有更好的方法来做到这一点?

标签: typescript

解决方案


考虑到根据您的定义,以下行将在没有警告的情况下编译Thing

const A: Thing = { thing2: undefined };

这在processObject() 技术上正确调用时会出错;当您只期望 a或 a时,您最终可能会undefined从对象中读取一个值。stringnumber


解释:

TypeScript 并不总是将缺少值(如对象属性或函数参数)的情况与存在值但undefined. 这种混淆是从 JavaScript 继承而来的,其中的区别可能很微妙:如果我有一个名为obj, 和obj.prop === undefinedis的 JavaScript 对象true,我无法从中判断"prop" in obj是真还是假。

GitHub 中有一个长期未解决的问题microsoft/TypeScript#13195,要求在区分“缺失”和undefinedTypeScript 方面保持一致。可选属性被视为“可能丢失”和“可能undefined”,而相反,不允许丢失定义中的必需属性| undefined。现在没有办法说“我想要一个可能丢失的属性,但如果它存在,它不应该存在”。undefined

此外,索引签名也受到相反方向的双重思考:实际上,您可以从中获取undefined属性值(因为可能缺少任何给定的属性)但编译器不承认这一点(并且表现得好像每个可能的属性都存在并定义)。请参阅microsoft/TypeScript#13778以了解undefined自动包含在索引签名属性域中的建议,以及有关它的无数评论。有一个即将推出的名为--noUncheckedIndexedAccess(参见microsoft/TypeScript#39560)的功能标志旨在解决此问题,但即使您打开它,您的代码仍然会遇到同样的问题,因此它并不能在所有情况下都完全修复它。

undefined在可选属性中处理缺失值和值的方式与在索引签名中处理它们的方式之间的这种不匹配是导致问题的原因。


在我看来,处理这个问题的方法就是将它添加undefined到您的索引签名中,并明确承认可能存在可选属性,但是undefined. 这并不理想,但至少更接近于一致。


Playground 代码链接


推荐阅读