如何在TypeScript中定义含预定义属性与任意类型属性的映射类型?
解决方案
你可以用映射类型排除$id键来实现这个需求,具体写法如下:
type Foo = { $id: string; } & { [K in string as Exclude<K, '$id'>]: number; };
或者更简洁的写法,结合Record和Exclude:
type Foo = { $id: string } & Record<Exclude<string, '$id'>, number>;
这样定义后,Foo类型的对象必须包含$id字符串属性,其他任意字符串键对应的属性都得是数值类型,完全符合你的要求:
const foo: Foo = { $id: 'id', bar: 123, baz: 456 // 合法 }; const invalidFoo: Foo = { $id: 123, // 报错:$id必须是字符串类型 bar: 'abc' // 报错:bar必须是数值类型 };
为啥之前的写法不行?
第一种写法的问题
type Foo = { $id: string; [key: string]: number; }
这里的索引签名[key: string]: number是对所有字符串键属性的强制约束,要求所有属性都得是number,但$id是string类型,直接和索引签名冲突,TypeScript肯定会报错。
第二种写法的问题
type Foo = { [key: string]: number; } & { $id: string };
这种交叉类型看似合理,但实际访问foo.$id时,TypeScript会把它解析成number & string,也就是never类型——毕竟一个值不可能既是字符串又是数字,这就导致$id属性根本没法正常用,完全不符合预期。
内容的提问来源于stack exchange,提问作者Lehks
相关产品推荐
相关产品推荐

