TypeScript:索引签名中使用扩展语法导致类型不兼容问题
解决TypeScript对象展开后的类型不匹配问题
先看你遇到的代码错误场景:
type Foo = { [key: string]: { bar: "bar", baz: string[], qux: string, }; } const obj = { bar: "bar", baz: ["baz"], }; const a: Foo = { key: { // error TS2322: Type '{ bar: string; baz: string[]; qux: string; }' is not assignable to type '{ bar: "bar"; baz: string[]; qux: string; }'. qux: "qux", ...obj, }, }; console.log(a);
错误核心:TypeScript会把obj.bar推断为宽泛的string类型,而Foo要求bar是精确的字面量类型"bar",导致展开后的对象类型不匹配。你尝试用as const后,obj.baz变成只读数组readonly ["baz"],又和Foo里的可变string[]类型冲突。
下面是几种保持强类型的解决方法:
方法一:给obj显式指定匹配的类型
直接约束obj的类型,让bar被限定为字面量"bar",baz保持可变string[]:
type Foo = { [key: string]: { bar: "bar", baz: string[], qux: string, }; } // 显式指定obj的类型为Foo的子类型 const obj: { bar: "bar"; baz: string[] } = { bar: "bar", baz: ["baz"], }; const a: Foo = { key: { qux: "qux", ...obj, }, };
方法二:给合并后的对象做类型断言
如果不想修改obj的定义,可以给展开后的对象做精确类型断言:
type Foo = { [key: string]: { bar: "bar", baz: string[], qux: string, }; } const obj = { bar: "bar", baz: ["baz"], }; const a: Foo = { key: { qux: "qux", ...obj, } as { bar: "bar"; baz: string[]; qux: string }, };
方法三:用类型工具灵活推导
通过Pick提取Foo的子类型字段,避免硬编码类型:
type Foo = { [key: string]: { bar: "bar", baz: string[], qux: string, }; } const obj = { bar: "bar", baz: ["baz"], }; // 提取Foo的子类型 type FooItem = Foo[string]; const a: Foo = { key: { qux: "qux", ...obj, } as Pick<FooItem, "bar" | "baz"> & { qux: string }, };
如果你想保留as const的用法
可以把只读数组转为可变数组,解决类型冲突:
type Foo = { [key: string]: { bar: "bar", baz: string[], qux: string, }; } const obj = { bar: "bar", baz: ["baz"], } as const; const a: Foo = { key: { qux: "qux", bar: obj.bar, // 通过展开运算符将只读数组转为可变数组 baz: [...obj.baz], }, };
内容的提问来源于stack exchange,提问作者silverwind
相关产品推荐
相关产品推荐

