我正在try 循环一个对象的属性,并覆盖相同类型的现有对象. 它可以在运行时运行,但TypeScrip说:

pet2[key]: Type 'string | number' is not assignable to type 'never'.ts(2322)

type Pet = { name: string; age: number };
const pet1: Pet = { name: 'Speedy', age: 4 };
const pet2: Pet = { name: 'Bibi', age: 5 };

// overwrite manually - no typescript error:
pet2['name'] = pet1['name'];
pet2['age'] = pet1['age'];

// overwrite with a loop - typescript error:
const keys = Object.keys(pet1) as Array<keyof Pet>;
for (const key of keys) 
    pet2[key] = pet1[key];

我怎样才能用循环来实现这一点,这样Typescript 就不会抱怨了?

在我看来,TypeScrip会判断赋值右侧的所有可能类型(字符串|数字),然后try 组合左侧的所有属性(字符串和数字),然后得出结论:(字符串|数字)不适合(从不).

编辑: 这只是我的问题的一个简化版本.我一次又一次地遇到这个问题.今天,我在一家超薄store 订阅了它:

for (const filterColum of filterColumns)
    selected[filterColum].subscribe(($value) => { $selected[filterColum] = $value })

推荐答案

这是打字的一个已知痛点,据报道,打字速度为microsoft/TypeScript#32693.当您有两个类型为Pet的对象pet1pet2,并且union type的键key可赋值给keyof Pet时,编译器通常不会让您编写

pet2[key] = pet1[key]; // error!

因为它没有注意到key在两边都是value这个事实.它会将其视为您可能有两个键key1key2,这两个键都属于相同的联合类型:

pet2[key2] = pet1[key1]; // error

你希望that是一个错误,因为如果keyof Pet"name" | "age",那么你想避免key1"name"key2"age"的情况,你给一个字符串属性分配了一个数字.

如果编译器能够区分这两个赋值,那就太好了,因为一个总是安全的,而另一个很少是安全的.但是,除非微软/TypeScrip#32693被解决,否则你将需要解决这个问题.


对于给定的示例,最简单的解决方法是忘记循环,只使用the Object.assign() method,就像Object.assign(pet2, pet1)一样.但是,如果您需要在代码中包含类似loplike的内容,那么这是行不通的.


对于希望编译器跟踪相关联合类型的这种情况,通常推荐的方法是重构以使用generics.因此,如果Obj[keyof Obj]不起作用,您可以try Obj[K],其中K extends keyof Obj:

(Object.keys(pet1) as Array<keyof Pet>).forEach(
  <K extends keyof Pet>(key: K) => {
    pet2[key] = pet1[key];
  });

这里我使用the forEach() method,因为它需要回调,这是放置泛型的方便位置.你可以坚持使用a for...of loop,但使用起来要笨重得多:

const keys = Object.keys(pet1) as Array<keyof Pet>;
for (const key of keys) {
  (<K extends keyof Pet>(key: K) => { pet2[key] = pet1[key] })(key);
}

如果这些都不是您喜欢的,您总是可以自由地使用type assertion来安抚编译器,例如下面的断言pet2the intentionally loose any type:

for (const key of keys)
  (pet2 as any)[key] = pet1[key];  // okay

请注意,通常建议not使用the //@ts-ignore compiler directive来使错误静默,除非这是您继续的唯一方法.//@ts-ignore指令就像在警示灯上贴了一条胶带;它实际上并没有解决错误,只是防止您看到它,并可能导致代码中其他地方发生奇怪的事情.它也不能用于使特定错误静默;同一行上的任何其他错误都将被忽略:

for (const key of keys)
  //@ts-ignore
  pet3[key] - pet1[kay]; // okay?!

您可能希望得到有关未声明变量或不正确运算符的某种警告.正如它在//@ts-ignore的手册文档中所说的,"我们建议您使用这些注释very sparingly".

Playground link to code

Typescript相关问答推荐

如何使用TypScript和react-hook-form在Next.js 14中创建通用表单组件?

如何从TypScript中的接口中正确获取特定键类型的所有属性?

类型脚本泛型最初推断然后设置

为什么TypeScript失go 了类型推断,默认为any?''

如何正确地对类型脚本泛型进行限制

泛型类型,引用其具有不同类型的属性之一

有没有办法解决相互影响的路由之间的合并?

如何使用WebStorm调试NextJS的TypeScrip服务器端代码?

如何创建一家Reduxstore ?

使用2个派生类作为WeakMap的键

如何将定义模型(在Vue 3.4中)用于输入以外的其他用途

为什么Typescript不能推断类型?

为什么我的查询钩子返回的结果与浏览器的网络选项卡中看到的结果不同?

APP_INITIALIZER在继续到其他Provider 之前未解析promise

使用条件类型的类型保护

是否可以通过映射类型将函数参数约束为预定义类型?

打字脚本中的模块化或命名空间

字符串文字联合的分布式条件类型

垫子工具栏不起作用的Angular 布线

为什么 Typescript 无法正确推断数组元素的类型?