TypeScript中如何将CustomRouteType数组映射为其返回值类型数组?
这个问题我太熟了!你想要的其实就是把数组里的每个构造器/函数类型,转换成它们的返回值类型数组——TypeScript确实没有现成的ReturnValues工具类型,但我们可以自己实现一个映射类型来搞定。
核心思路
我们需要遍历TTypes数组中的每个元素,对每个元素提取它的返回值类型,再重新组成一个新的数组类型。TypeScript的映射类型和ReturnType工具类型可以完美配合完成这个任务。
具体实现步骤
1. 定义数组元素返回值类型的映射类型
首先,我们写一个工具类型InstanceTypes,它接收一个CustomRouteType数组类型,然后返回对应的返回值类型数组:
type InstanceTypes<T extends CustomRouteType[]> = { [K in keyof T]: ReturnType<T[K]>; };
这个类型的作用是:遍历输入数组T的每个索引K,取出该位置的元素类型T[K],用ReturnType获取这个函数类型的返回值,最终组成一个和原数组结构一致的新类型数组。
2. 修改createRoute的类型定义
接下来,把createRoute的回调参数类型从...params: TTypes改成...params: InstanceTypes<TTypes>,这样就能自动把构造器类型映射成对应的返回值类型了:
const createRoute = <TTypes extends CustomRouteType[]>( dictionary: RouteDictionary<TTypes>, callback: (...params: InstanceTypes<TTypes>) => void ) => { // 你的逻辑代码 };
3. 验证效果
现在再调用createRoute时,TypeScript就能正确推断回调参数的类型了:
createRoute( route`/api/someEndpoint/${Number}/${String}/test`, (p1, p2) => { // p1 自动推断为 number 类型 // p2 自动推断为 string 类型 const num: number = p1; // ✅ 类型匹配 const str: string = p2; // ✅ 类型匹配 } );
如果是自定义的CustomRouteType,也能正常工作:
// 自定义一个返回boolean的路由参数解析函数 const booleanParam = <T>(value?: T): boolean => !!value; createRoute( route`/api/${booleanParam}`, (isValid) => { // isValid 自动推断为 boolean 类型 const flag: boolean = isValid; // ✅ 类型匹配 } );
完整修改后的代码
结合你提供的原有代码,完整版本如下:
type CustomRouteType<TParam = any> = <T>(value?: T) => TParam; class RouteString { constructor(public routeString: string) {} } class RouteParameter<T = any> { constructor(public routeString: CustomRouteType<T>) {} } type RouteDictionary<TTypes extends CustomRouteType[]> = { routeParts: (RouteString | RouteParameter)[]; types: TTypes; }; const route = <TTypes extends CustomRouteType<any>[]>( template: TemplateStringsArray, ...keys: TTypes ): RouteDictionary<TTypes> => { let routeParts: (RouteString | RouteParameter)[] = []; const stringParts = template.raw || []; for (let partIndex = 0; partIndex < stringParts.length; partIndex++) { const part = stringParts[partIndex]; const stringChunks = part.split('/').filter(chunkPart => chunkPart !== String()); routeParts = [...routeParts, ...stringChunks.map(chunk => new RouteString(chunk))]; if (keys.length === 0) continue; if (partIndex >= keys.length) continue; const routeParam = keys[partIndex]; if (!routeParam) continue; routeParts = [...routeParts, new RouteParameter(routeParam)]; } return { routeParts, types: keys }; }; // 新增的映射类型 type InstanceTypes<T extends CustomRouteType[]> = { [K in keyof T]: ReturnType<T[K]>; }; // 修改后的createRoute const createRoute = <TTypes extends CustomRouteType[]>( dictionary: RouteDictionary<TTypes>, callback: (...params: InstanceTypes<TTypes>) => void ) => { // 这里可以添加你的逻辑,比如解析路由参数后调用callback }; // 测试调用 createRoute( route`/api/someEndpoint/${Number}/${String}/test`, (p1, p2) => { console.log(p1.toFixed(2)); // p1是number,支持number的方法 console.log(p2.toUpperCase()); // p2是string,支持string的方法 } );
这个方案的核心就是利用TypeScript的映射类型能力,把数组中的每个函数类型的返回值提取出来,重新组合成新的参数类型数组,完美解决你遇到的类型推断问题。
内容的提问来源于stack exchange,提问作者Marvin Brouwer
相关产品推荐
相关产品推荐

