TypeScript中如何根据传入参数类型匹配对应函数返回值类型
你定义的IRedisGet是两个独立函数类型的联合,TypeScript 校验这类联合类型的函数实现时,要求实现必须同时满足联合中每一个函数签名的约束——换句话说,TS 不会顺着你代码里的if/else分支把入参类型和对应分支的返回值做关联绑定,只会把所有分支的返回值合并成string | number的联合类型,自然匹配不上单个签名要求的「传'a'必须返回string」「传'b'必须返回number」的约束,就会抛出你看到的类型错误。顺带提下你代码里的函数名redistGet是拼写笔误,正确应为redisGet,不过这个问题不影响类型报错的产生。
有两种成熟的写法可以实现入参和返回值类型一一绑定的效果,根据场景选择即可:
写法1:函数重载(兼容性最好,逻辑直观)
这是TS官方推荐的这类场景的标准写法,先对外暴露所有精确的入参返回值配对,再写宽泛的内部实现签名:
type IA = 'a' type IAData = string; type IB = 'b' type IBData = number; // 重载签名:对外暴露的精确类型约束 export function redisGet(key: IA): IAData; export function redisGet(key: IB): IBData; // 实现签名:仅内部使用,类型可以放宽,不会对外暴露 export function redisGet(key: IA | IB): IAData | IBData { if (key === 'a') { return 'a'; } else if (key === 'b') { return 12; } throw new Error('invalid key'); }
使用时TS会自动匹配重载:传'a'时返回值类型推导为string,传'b'时推导为number,完全符合预期。
写法2:泛型+类型映射(适合键值对多、扩展性要求高的场景)
如果后续要加的key很多,堆重载签名会很冗余,可以先维护一个key到返回值类型的映射表,用泛型自动推导对应返回类型:
// 统一维护key和对应返回值的映射关系 type RedisDataMap = { a: string; b: number; } type IRedisGet = <K extends keyof RedisDataMap>(key: K) => RedisDataMap[K]; export const redisGet: IRedisGet = (key) => { switch(key) { case 'a': return 'a' as RedisDataMap[K]; case 'b': return 12 as RedisDataMap[K]; default: throw new Error('invalid key'); } }
实现里加类型断言是因为TS目前没法在泛型场景下跨分支确认返回值和K的绑定关系,只要分支逻辑和映射表对应,这个断言是完全类型安全的。后续要加新的key类型,只需要在RedisDataMap里加一行配置即可,不用修改函数类型定义。
注意:不要尝试用函数类型联合实现这类需求,TS对函数联合的实现校验逻辑是取所有函数签名的交集,根本做不到入参和返回值的关联匹配。
内容的提问来源于stack exchange,提问作者Noitidart

