如何从泛型箭头函数中提取泛型类型?——tRPC路由类型合并场景下的类型处理需求
看起来你在tRPC虚拟路由类型合并的场景里卡壳了,核心问题是怎么把原本是泛型箭头函数的tRPC_Router_Type,改成能手动指定泛型参数的类型,对吧?而且因为tRPC有些内部类型没对外导出,没法直接重写整个路由构建函数的类型定义——这个需求其实可以通过TypeScript的infer关键字轻松解决,咱们一步步来拆解:
先从你的简单类比例子入手
你给的简化版问题是:把type ArrowFn = <TInput extends string>(input: TInput) => {}转成能手动传泛型的type GenericFn<TInput extends string> = (input: TInput) => {}。这里的关键是用infer把箭头函数的泛型参数「提升」到类型本身的泛型上:
// 定义一个工具类型:把泛型箭头函数转成「类型本身带泛型」的函数类型 type ExtractGenericFn<Fn> = Fn extends <TInput extends infer TConstraint>(input: TInput) => infer TReturn ? <T extends TConstraint>(input: T) => TReturn : never; // 测试你的简化例子 type ArrowFn = <TInput extends string>(input: TInput) => { value: TInput }; // 转成可手动指定泛型的类型 type ConfigurableFn = ExtractGenericFn<ArrowFn>; // 现在ConfigurableFn就等价于:<T extends string>(input: T) => { value: T } // 如果要直接传入参数类型得到结果类型: type FnResult = ReturnType<ConfigurableFn<"test">>; // FnResult 就是 { value: "test" }
套回你的tRPC实际场景
回到你遇到的tRPC问题:你的tRPC_Router_Type是一个泛型箭头函数,而且里面包含tRPC未导出的内部类型(比如DecorateCreateRouterOptions),咱们完全不用管这些内部类型,用infer让TypeScript自动帮我们捕获它们就行:
步骤1:定义提取泛型箭头函数的工具类型
// 工具类型:把泛型箭头函数转成可手动指定泛型的函数类型 type ExtractRouterBuilder<BuilderFn> = BuilderFn extends <TInput extends infer TConstraint>(input: TInput) => infer TRouter ? <T extends TConstraint>(input: T) => TRouter : never;
这里的TConstraint会自动捕获tRPC_Router_Type里泛型参数的约束(也就是CreateRouterOptions),TRouter会自动捕获路由构建后的返回类型(包括那些未导出的内部类型),完全不用我们手动写那些摸不着的内部类型定义。
步骤2:转换你的tRPC路由构建函数类型
// 你原本的泛型箭头函数类型 type tRPC_Router_Type = <TInput extends CreateRouterOptions>(input: TInput) => BuiltRouter< { ctx: My_tRPC_Context_Type; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<...> // 这个未导出的类型不用管,infer会帮我们抓 >; // 转成可手动指定泛型的路由构建器类型 type ConfigurableRouterBuilder = ExtractRouterBuilder<tRPC_Router_Type>;
步骤3:生成全局虚拟路由类型
// 你的各个服务路由类型集合 type MyServiceRoutersTypes = { service1: typeof router1, service2: typeof router2 }; // 传入服务路由类型,得到合并后的全局虚拟路由类型 export type GlobalVirtualRouter = ReturnType<ConfigurableRouterBuilder<MyServiceRoutersTypes>>;
为什么这能行?
核心在于infer的「类型捕获」能力:它不需要我们知道tRPC内部未导出类型的具体结构,只需要让TypeScript帮我们完整保留这些类型信息就行。通过把泛型箭头函数的泛型参数「提升」到类型本身的泛型上,我们就能手动传入MyServiceRoutersTypes,最终得到类型安全的全局虚拟路由类型,客户端消费时就能获得完整的类型提示。
内容来源于stack exchange

