使用工厂函数实现联合类型、鉴别符与Partial<T>的类型收窄
TypeScript 鉴别符工厂函数的类型安全实现
问题背景
需要实现一个工厂函数,通过类型鉴别符和Partial<T>传入的属性子集创建子类型实例,但原代码存在两个问题:
- 调用时无法正确推断返回类型(比如传入'A'应该返回TypeA,但实际是TypeA | TypeB)
- 传入不符合对应类型的属性时不会触发类型错误(比如给'B'传aProp不会报错)
原代码示例:
type TypeA = { discriminator: 'A'; aProp: string; }; type TypeB = { discriminator: 'B'; bProp: string; }; type TypeX = TypeA | TypeB; // 联合类型 type TypeDiscriminator = TypeX['discriminator']; // "A" | "B" function createInstance(discriminator: TypeDiscriminator, partial: Partial<TypeX>) { switch (discriminator) { case 'A': return { ...partial, discriminator: 'A' } as TypeA; case 'B': return { ...partial, discriminator: 'B' } as TypeB; default: throw Error(); } } // 问题:此处应推断为TypeA,但实际是TypeA | TypeB export const a = createInstance('A', { aProp: 'foobar' }); // 此代码可运行但无效(aProp不存在于TypeB中,应触发错误) export const b = createInstance('B', { aProp: 'foobar' }) as TypeB;
解决方案(不修改参数列表结构)
核心是通过泛型+映射类型把鉴别符和对应子类型绑定,让TS能根据传入的鉴别符自动约束partial参数的类型,并正确推断返回值。
修改后的完整代码:
type TypeA = { discriminator: 'A'; aProp: string; }; type TypeB = { discriminator: 'B'; bProp: string; }; type TypeX = TypeA | TypeB; type TypeDiscriminator = TypeX['discriminator']; // 新增映射类型:把鉴别符映射到对应的子类型 type TypeMap = { 'A': TypeA; 'B': TypeB; }; // 改为泛型函数,关联鉴别符和对应类型 function createInstance<K extends TypeDiscriminator>( discriminator: K, partial: Partial<TypeMap[K]> ): TypeMap[K] { switch (discriminator) { case 'A': return { ...partial, discriminator: 'A' } as TypeMap[K]; case 'B': return { ...partial, discriminator: 'B' } as TypeMap[K]; default: throw new Error(`未知鉴别符: ${discriminator}`); } } // 现在正确推断为TypeA类型 export const a = createInstance('A', { aProp: 'foobar' }); // 此处会触发TypeScript错误,符合预期(aProp不属于TypeB) // export const b = createInstance('B', { aProp: 'foobar' });
逻辑说明
TypeMap建立了鉴别符字符串和具体子类型的一一对应关系,让TS能通过鉴别符找到对应的类型- 泛型
K被约束为TypeDiscriminator,确保传入的鉴别符只能是'A'或'B' partial参数被指定为Partial<TypeMap[K]>,意味着必须传入对应子类型的属性子集,不符合的属性会直接报错- 返回值明确为
TypeMap[K],TS能根据传入的鉴别符自动推断出具体的子类型,无需手动断言
内容的提问来源于stack exchange,提问作者Dynalon
相关产品推荐
相关产品推荐

