TypeScript对象数组区分类型未生效的问题及解决方法
问题描述
我定义了以下TypeScript类型:
enum Prevals { one = 'one', two = 'two', three = 'three', } type PrevalParams = { [Prevals.one]: { name: string; age: number; }; [Prevals.two]: { t: 'thing'; }; [Prevals.three]: undefined; } type PrevalConfig<Id extends Prevals> = { id: Id; level: 'error' | 'warning'; params: PrevalParams[Id]; };
我希望将多个PrevalConfig对象存入数组时,TypeScript能校验每个对象的params与对应id严格匹配,但当前使用PrevalConfig<Prevals>[]作为数组类型时,params会被解析为所有参数类型的联合,导致错误代码无法触发校验。比如下面这段明显不匹配的代码没有报错:
const prevals: PrevalConfig<Prevals>[] = [ { id: Prevals.one, level: 'warning', params: { t: 'thing' } } ]
解决方案
问题出在PrevalConfig<Prevals>是将Id设为枚举值的联合类型,导致params变成所有PrevalParams值的联合,TypeScript会认为只要params属于这个联合就合法,不管和id是否对应。
要实现精确校验,需要让数组的每个元素是每个具体枚举值对应的PrevalConfig的联合类型,而不是给泛型传入联合类型。可以通过映射类型生成这个联合:
type ValidPrevalConfig = { [K in Prevals]: PrevalConfig<K> }[Prevals];
然后将数组类型设为ValidPrevalConfig[],此时TypeScript会对数组中的每个元素做精确的类型校验:
// 正确示例:每个对象的params和id匹配 const validPrevals: ValidPrevalConfig[] = [ { id: Prevals.one, level: 'warning', params: { name: 'Alice', age: 30 } }, { id: Prevals.two, level: 'error', params: { t: 'thing' } }, { id: Prevals.three, level: 'warning', params: undefined } ]; // 错误示例:params和id不匹配,TypeScript会触发报错 const invalidPrevals: ValidPrevalConfig[] = [ { id: Prevals.one, level: 'warning', params: { t: 'thing' } // 此处报错:类型"{ t: 'thing'; }"不能赋值给类型"{ name: string; age: number; }" } ];
原理说明
{ [K in Prevals]: PrevalConfig<K> }会生成一个映射类型,其中每个键是Prevals的枚举值,对应的值是该枚举值对应的PrevalConfig类型。之后通过[Prevals]索引访问,会将这个映射类型的所有值提取出来形成联合类型。这个联合类型是分布式的,TypeScript会对数组中的每个元素单独匹配联合中的每个分支,从而确保id和params的对应关系。
内容的提问来源于stack exchange,提问作者kael
相关产品推荐
相关产品推荐

