TypeScript中使用解构语法组装新对象报错,求助问题原因与解决方法
嗨,我完全懂你的困惑!毕竟TypeScript自称是JavaScript的超集,为啥JS里能正常跑的代码到TS这儿就抛出TS18004错误了呢?咱们先搞清楚问题出在哪,再给你几个靠谱的解决办法。
问题原因
首先得纠正一个小误解:你那行JS代码的实际行为可能和你以为的不太一样!({a, c} = objOrig)是一个赋值表达式,它的返回值是整个objOrig对象,而不是只包含a和c的新对象。所以你JS里的objChanged其实等价于{ ...objOrig, b: 'S' }——只是刚好覆盖了b,得到了你想要的结果。
而TypeScript的语法检查比JS严格得多:在对象字面量的展开语法中,TS会优先把{a, c}解析成shorthand 属性(即引用当前作用域中名为a和c的变量),而不是解构赋值的一部分。但你当前作用域里并没有声明a和c变量,所以就抛出了No value exists in scope for the shorthand property 'a'的错误。
解决方法
根据你的需求(从原对象中选取部分属性,再修改特定属性生成新对象),这里有几种符合TS规范的写法:
方法1:先解构再组装(最清晰易读)
把解构步骤单独抽出来,TS能完美识别,代码可读性也最高:
type S = {a: number; b: number| string; c: number}; const objOrig: S = { a: 1, b: 2, c: 3}; // 先从原对象解构出需要的属性 const { a, c } = objOrig; // 组装新对象,直接修改b属性 const objChanged: S = { a, c, b: 'S' };
方法2:用立即执行函数包裹解构(保持紧凑写法)
如果你想保持类似JS的紧凑风格,可以用立即执行函数把解构逻辑封装起来,让TS明确这是一个返回目标对象的表达式:
type S = {a: number; b: number| string; c: number}; const objOrig: S = { a: 1, b: 2, c: 3}; const objChanged: S = { // 立即执行函数接收原对象,解构出a和c后返回包含这两个属性的对象 ...(({ a, c }) => ({ a, c }))(objOrig), b: 'S' };
方法3:使用TS工具类型Pick(更贴合TS风格)
TS内置的Pick工具类型可以帮你从原对象中精准选取需要的属性,代码更有TS味儿:
type S = {a: number; b: number| string; c: number}; const objOrig: S = { a: 1, b: 2, c: 3}; // 用Pick<S, 'a' | 'c'>提取原对象的a和c属性 const objChanged: S = { ...(objOrig as Pick<S, 'a' | 'c'>), b: 'S' };
或者如果你想更严谨,可以封装一个简单的pick函数(避免类型断言):
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> { const result = {} as Pick<T, K>; keys.forEach(key => { result[key] = obj[key]; }); return result; } // 调用方式: const objChanged: S = { ...pick(objOrig, ['a', 'c']), b: 'S' };
总结
TS的严格检查虽然偶尔会“卡”住JS里的宽松写法,但本质是帮我们避免潜在的逻辑歧义。上面几种方法都能实现你的需求,其中方法1最适合日常开发,可读性拉满;如果追求紧凑写法可以选方法2;想贴合TS类型系统就选方法3。
备注:内容来源于stack exchange,提问作者F.A.M.

