首页 > 解决方案 > 为什么 TypeScript 不能推断出相同对象类型的赋值?

问题描述

假设我有一个非常简化的示例来说明我的代码中发生的事情:

interface I {
  n?: number;
  s?: string;
}

const a: I = {
  n: 1,
}

const b: I = {
  n: 2,
  s: 'b',
}

const props = ['n', 's'] as const;

for (const prop of props) {
  if (!a[prop]) {
    // here, I get the following error:
    // TS2322: Type 'string | number' is not assignable to type 'never'.
    //   Type 'string' is not assignable to type 'never'.
    a[prop] = b[prop];
  }
}

存在ab相同的类型,访问相同的属性prop......这不是可能的吗?

那么,我在这里缺少什么?

更新:由于我简化了我的例子太多,答案是删除as const部分......但我认为这是由于我的环境,因为我正在strict: true使用tsconfig.json......

然后,如果我尝试在没有 的情况下访问as const,我会收到 TS7053 错误:

元素隐式具有“任何”类型,因为“字符串”类型的表达式不能用于索引类型“I”。

在“I”类型上未找到具有“字符串”类型参数的索引签名。

修复添加as const或做for(const prop of props as ('n' | 's')[]) {

然后是当我得到我原来的错误时(可以很容易地修复,as any但我试图避免这种情况)

标签: typescript

解决方案


一般来说,Typescript 不会证明代码的所有可证明属性,也不会尝试证明。在这种特殊情况下,我们人类知道类型与类型b[prop]匹配,a[prop]因为属性名称相同,但 Typescript 看不到这一点,因为它没有允许它的规则。

如果我们按照 Typescript 的逻辑:

  • props被声明为类型的元组['n', 's']
  • prop被声明为 的元素props,因此其类型被推断为'n' | 's'
  • 由于b被声明为 type I, thenb[prop]被推断为 typeI['n' | 's']简化为string | number
  • 由于a被声明为 type I,那么赋值目标 a[prop]只能接收一个可赋值给任何prop可能命名的属性的值;因此,分配目标的类型被推断为I['n'] & I['s']简化为string & number,然后never
  • 就 Typescript 而言,赋值a[prop] = b[prop]是将 type 的值赋给 typestring | number的赋值目标never。由于string | number不可分配给never,这是一个类型错误。

最简单的解决方法是使用类似的类型断言b[prop] as any,即告诉 Typescript 你知道你的代码是类型安全的,因此不需要检查它。另一种选择是使用辅助函数,它以满足 Typescript 类型检查器的方式进行分配:

function copyMissingProperties<T>(a: T, b: T, props: readonly (keyof T)[]): void {
    for (const prop of props) {
        if (!a[prop]) {
            a[prop] = b[prop]; // ok
        }
    }
}

这里,prop' 的类型keyof T不是联合类型,因此赋值目标没有交集类型。


推荐阅读