ts循环赋值断言也报错?-灵析社区

代码日记

type PersonType = typeof person function cloneObj(obj: PersonType) { const newObj = {} as PersonType for (const key of Object.keys(obj)) { // 报错 为啥会有never类型 // 类型"string|number"不可分配给类型"never"。 // 类型"string"不可分配给类型"never"。 newObj[key as keyof PersonType] = obj[key as keyof PersonType] } return newObj } const person = { name: '曜', sex: '男', age: 16 // 如果属性值都是字符串就不报错 } cloneObj(person)

阅读量:328

点赞量:18

问AI
挺有意思的一个问题。 首先要指出,题主这种写法,在 TS 3.4 之前是不会报错的。而在 TS 3.5 之后引入了一项名为 "Fixes to Unsound Writes to Indexed Access Types" (https://link.segmentfault.com/?enc=C7ijwGKrq6jAIdi734Cjfw%3D%3D.RwL1N1h2X2jNOf%2BP1XxQzerDaCiwG6s8hjCtYmampklg1XTxc6iv%2Bw6UXgQnTjyHKDIEFXyr%2FeFB%2BzSiIDIrJVy1MX%2B%2FfvOJmgsWqXPOv8Fr1Oua3e9nlHUMjnrUqaCWjnfPJGpp13pGcDiayvR%2FsA%3D%3D) 的破坏性变更后,才会报错。 相关的讨论有很多,比如 "#30769" (https://link.segmentfault.com/?enc=r1XpR1vXVQfOWT7qBzupTg%3D%3D.5vrc9tZrSLK%2BkeCAqKtmjRCyaUI8BxXgdK%2Bus3qICGpkYrWnmS1SnnFQfGDg%2BcYGEwylx34RA9DnF45lCL7%2FbQ%3D%3D)、"#33834" (https://link.segmentfault.com/?enc=0i0Gl1Sot7KV6LgadjfKgA%3D%3D.4uDkaD9XlN0j%2F2zIZuxpbp13UwjGFx3yESgHO4TlJvGCJEe7%2FYIBkr4bCPwvM4u%2BYsCuUxnceA1ZVfdZjWf29Q%3D%3D)。 那么为什么 TS 要把这种写法视为一种错误呢? 其实如果你只是取值(即 Read),是没问题的: let v = obj[key as keyof PersonType]; // ok 此时 "v" 的类型会推断为 "string | number",也就是 "PersonType" 中所有属性的联合类型 。 问题出现在赋值(即 Write)上。 我们可以看出,其实 "PersonType" 上每个属性的类型,是确定的,要么是 "string"、要么是 "number",并不真的存在一个属性的类型是 "string | number"。因此我们在尝试下面的写法时,才会得到错误,这是符合预期的: person.name = 16; // error person.age = '曜'; // error 但如果你用了索引属性去赋值,那么就会出现: let key = 'name'; person[key as keyof PersonType] = 16; let key = 'age'; person[key as keyof PersonType] = '曜'; 就打破了上面这种预期,也就是所谓的 Unsound Writes 。 因此 TS 3.5 之后引入了这项破坏性变化,当你尝试这么做的时候,会把类型收窄为所有属性的交叉类型 ,只有满足此交叉类型的,才能正确赋值。具体到本题中得到的交叉类型也就是 "string & number",但很显然,没有任何类型 "T" 是能满足 "T extends string & number" 的,因此得到了 "never",故而抛出题中的异常。 *** 改法有很多种,但我只推荐用泛型。 除非你这个 "cloneObj" 专门针对 "PersonType" 编写的,否则我建议你这么写: function cloneObj>(obj: T) { const newObj = {} as T; for (const key of Object.keys(obj)) { newObj[key as keyof T] = obj[key as keyof T]; } return newObj; }