TypeScript限制泛型为单个字符串字面量 禁止联合类型的方法
问题解决方案
TypeScript 没有原生语法可以直接在泛型约束层面禁止传入联合类型,但可以通过两种成熟方案实现type和fieldProperty字段的严格一一对应,从根源上避免不合法实例通过编译。
方案1:使用分布式判别联合(推荐)
这是业务场景下最常用、体验最好的方案,不需要手动传入泛型参数,直接通过映射类型生成所有合法实体的联合类型,自动完成类型分发,从类型定义层面排除字段不匹配的可能。
实现代码
type EntityTypes = 'foo' | 'bar' | 'baz'; type EntityMappings = { foo: string; bar: number; baz: Array<string>; } // 遍历所有实体类型,生成每个单类型对应的合法结构,再通过索引得到联合类型 type Entity = { [K in EntityTypes]: { type: K; fieldProperty: EntityMappings[K]; } }[EntityTypes];
效果验证
- 合法用例可正常通过编译:
const instance: Entity = { type: 'foo', fieldProperty: 'hello', }; const otherInstance: Entity = { type: 'baz', fieldProperty: ['a', 'b', 'c'], }
- 原问题中的非法用例会直接触发类型报错:
// 编译报错:类型"blah"不能赋值给Array<string>类型 const badInstance: Entity = { type: 'baz', fieldProperty: 'blah', };
额外收益
该方案生成的Entity是标准的TS判别联合类型,后续逻辑中通过判断type字段值,TS可以自动缩窄fieldProperty的类型,不需要额外的类型断言:
function process(e: Entity) { if(e.type === 'bar') { // TS自动推导e.fieldProperty为number类型,可安全调用数字方法 console.log(e.fieldProperty.toFixed(2)); } }
方案2:给泛型加联合类型检测(需保留泛型场景使用)
如果你确实需要保留GenericEntity的泛型形式,可以通过工具类型检测传入的泛型参数是否为联合类型,如果是则返回never阻断非法赋值。
实现代码
// 联合类型检测工具:传入单字面量返回false,传入联合类型返回true type IsUnion<T, U = T> = T extends unknown ? [U] extends [T] ? false : true : never; type GenericEntity<T extends EntityTypes> = IsUnion<T> extends true ? never // 可自定义错误类型,比如通过特殊字段提示"禁止传入联合类型" : { type: T; fieldProperty: EntityMappings[T]; }
效果验证
传入联合类型作为泛型参数时,得到的类型为never,任何赋值都会触发编译错误:
// 编译报错:不能将类型"{type: "baz", fieldProperty: string}"赋值给类型"never" const badInstance: GenericEntity<'foo' | 'baz'> = { type: 'baz', fieldProperty: 'blah', };
单字面量泛型参数的合法用例不受影响。
内容的提问来源于stack exchange,提问作者thorsday
相关产品推荐
相关产品推荐

