如何基于动态插件缩小TypeScript推断的泛型类型?
实现路由定义驱动的Context类型缩小方案
完全可以实现,核心是利用TypeScript的泛型、条件类型和映射类型,让server.route方法根据传入的路由定义自动推导对应的Context子集类型,具体方案如下:
1. 定义插件的类型契约
每个插件需要明确自身的路由配置要求和对应的Context注入类型,用通用类型约束插件的类型边界:
// 通用插件类型:关联路由配置与Context注入内容 type Plugin<RouteConfig extends object, ContextAddon extends object> = { requiredConfig: RouteConfig; injectedContext: ContextAddon; }; // 示例:认证插件的类型定义 type AuthPlugin = Plugin<{ auth: true }, User<AuthOptions>>; // 示例:校验插件的类型定义 type ValidationPlugin = Plugin<{ validation: ValidationOptions }, ValidationResult<ValidationOptions>>;
2. 构建路由配置到Context的类型映射
通过条件类型,根据路由定义中包含的插件配置,提取对应的Context注入部分:
// 根据路由定义推导Context类型 type RouteConfigToContext<RouteDef> = // 如果路由包含认证配置,注入User上下文 RouteDef extends AuthPlugin['requiredConfig'] ? AuthPlugin['injectedContext'] : {} & // 如果路由包含校验配置,注入ValidationResult上下文 RouteDef extends ValidationPlugin['requiredConfig'] ? ValidationPlugin['injectedContext'] : {};
3. 改造server.route方法的泛型定义
让server.route接收路由定义的泛型参数,自动关联对应的Context类型:
interface Server { route<RouteDef extends object>(options: { config: RouteDef; // 这里的ctx类型会根据RouteDef自动缩小 handler: (ctx: RouteConfigToContext<RouteDef>) => void; }): void; }
4. 多插件场景的批量处理(进阶)
如果插件数量较多,手动编写条件类型效率低,可以用映射类型+联合转交叉的辅助类型批量处理:
// 定义所有插件的配置-Context映射集合 type PluginMap = { auth: { config: { auth: true }; context: User<AuthOptions> }; validation: { config: { validation: ValidationOptions }; context: ValidationResult<ValidationOptions> }; // 新增插件直接在这里扩展 }; // 提取路由定义匹配的所有Context注入类型 type ExtractMatchedContexts<RouteDef> = { [K in keyof PluginMap]: RouteDef extends PluginMap[K]['config'] ? PluginMap[K]['context'] : {}; }[keyof PluginMap]; // 辅助类型:将联合类型转为交叉类型,合并多个Context注入 type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; // 最终的路由到Context的类型映射 type RouteConfigToContext<RouteDef> = UnionToIntersection<ExtractMatchedContexts<RouteDef>>;
效果演示
当传入不同的路由定义时,Context类型会自动缩小:
// 仅带认证配置:ctx类型为User<AuthOptions> server.route({ config: { auth: true }, handler: (ctx) => { console.log(ctx.user.id); // 类型安全,无报错 } }); // 同时带认证和校验配置:ctx类型为User<AuthOptions> & ValidationResult<ValidationOptions> server.route({ config: { auth: true, validation: { schema: userSchema } }, handler: (ctx) => { console.log(ctx.user.id, ctx.validatedData); // 均能正确识别类型 } });
内容的提问来源于stack exchange,提问作者robertrosman
相关产品推荐
相关产品推荐

