TypeScript中如何基于联合类型参数构建类型安全的函数参数?
解决TypeScript联合类型参数的类型匹配问题
先看给定的联合类型定义:
type Animals = | { key: 'dog'; value: { woof: string }; } | { key: 'cat'; value: { meow: number }; }
我们需要写一个函数fn,要求调用时必须传入与key对应的正确value结构:
- 合法调用示例:
fn('dog', { woof: 'woof' }); fn('cat', { meow: 123 }); - 以下非法调用必须在编译时报错:
fn('dog', { meow: 'meow' }); fn('cat', { woof: 'woof' });
你之前尝试的写法:
const fn = <T extends Animals>(key: T['key'], value: Extract<T, { key: T['key'] }>['value']) => { /* ... */ }
问题在于TypeScript无法通过这个泛型约束精准推导value的专属类型,最终value还是会被解析成联合类型{ woof: string } | { meow: number },达不到预期的约束效果。
几种可行的解决方案
方案1:泛型约束到联合类型的key,精准推导value
把泛型参数直接约束为Animals中的key类型,再通过Extract提取对应key的value类型:
type AnimalKey = Animals['key']; const fn = <K extends AnimalKey>( key: K, value: Extract<Animals, { key: K }>['value'] ) => { // 这里写函数逻辑 };
这样调用时,TypeScript会根据传入的key自动锁定对应的value类型,非法参数组合会直接触发编译错误。
方案2:使用函数重载
如果觉得泛型写法不够直观,可以用函数重载明确每个合法的参数组合:
// 定义重载签名 function fn(key: 'dog', value: { woof: string }): void; function fn(key: 'cat', value: { meow: number }): void; // 实现签名(仅在函数内部可见) function fn(key: Animals['key'], value: Animals['value']) { // 这里写函数逻辑 }
每个重载对应一种合法的调用方式,非法调用会因为匹配不到任何重载签名而报错。
方案3:先构建key-value映射类型
先把Animals联合类型转换成键值对形式的映射类型,再基于这个映射定义函数:
// 把Animals转换成key到value的映射 type AnimalValueMap = { [A in Animals as A['key']]: A['value'] }; const fn = <K extends keyof AnimalValueMap>( key: K, value: AnimalValueMap[K] ) => { // 这里写函数逻辑 };
这种方式通过映射类型把联合类型拆解成更直观的键值结构,同样能实现参数类型的精准匹配。
内容的提问来源于stack exchange,提问作者Radek Strnad
相关产品推荐
相关产品推荐

