在TypeScript条件类型中筛选匹配的组件类
问题核心
你要做的是从Angular路由配置里,精准提取指定组件对应的所有resolve数据类型——本质是TypeScript的类型编程问题。目前卡壳在两个地方:
- 用type-fest的
IsEqual对比组件类时,总是返回false,完全不符合预期 - 自己写的
ExtractRouteData类型逻辑不对,没法实现「UserComponent只能访问this.data.user,BookComponent只能访问this.data.book」的需求
为啥IsEqual不好使?
IsEqual是用来做结构类型深度对比的,但Angular组件的构造函数类型(比如typeof UserComponent)属于「名义类型」——它的唯一性是由自身标识决定的,不是结构。用IsEqual去比,会因为展开后原型链之类的细节差异返回false,完全没必要用它,直接比构造函数类型本身就行。
直接能用的解决方案
不用依赖第三方工具,用TypeScript内置的类型工具就能搞定核心逻辑:先从路由数组里筛出组件匹配的项,再提取对应的resolve类型。
完整示例代码
// 模拟Angular的Route类型(和官方类型对齐) type Route = { path: string; component: new (...args: any[]) => any; resolve?: Record<string, (...args: any[]) => any>; }; // 你的组件 class UserComponent {} class BookComponent {} // 你的resolve函数 const resolveUser = () => ({ id: 1, name: 'Alice' }); const resolveNewUser = () => ({ id: 2, name: 'Bob' }); const resolveBook = () => ({ isbn: '123', title: 'TypeScript Guide' }); // 路由配置(必须用字面量元组,别写成宽泛的Route[],不然TS会丢失具体类型信息) type AppRoutes = [ { path: 'user/:id', component: typeof UserComponent, resolve: { user: typeof resolveUser } }, { path: 'user/new', component: typeof UserComponent, resolve: { user: typeof resolveNewUser } }, { path: 'book/:isbn', component: typeof BookComponent, resolve: { book: typeof resolveBook } } ]; // 核心类型:提取指定组件对应的resolve数据类型 type ExtractRouteData<Routes extends Route[], TargetComponent> = // 先从路由数组里筛出component匹配的项 Extract<Routes[number], { component: TargetComponent }> extends infer MatchedRoutes ? // 提取这些路由的resolve类型,没有resolve就返回never MatchedRoutes extends { resolve: infer R } ? R : never : never; // 测试:UserComponent对应的resolve类型 type UserRouteData = ExtractRouteData<AppRoutes, typeof UserComponent>; // 结果:{ user: typeof resolveUser } | { user: typeof resolveNewUser } // 对应组件里this.data.user会被推断为两个resolve函数返回值的联合类型,完全符合预期 // 测试:BookComponent对应的resolve类型 type BookRouteData = ExtractRouteData<AppRoutes, typeof BookComponent>; // 结果:{ book: typeof resolveBook } // 组件里只能访问this.data.book,访问this.data.user会直接报错
关键细节
- 路由配置必须是字面量元组:如果写成
const routes: Route[] = [...],TS会把它当成宽泛的数组类型,丢失每个路由的具体component和resolve信息,类型提取就失效了。 - 用Extract替代IsEqual:
Extract<Routes[number], { component: TargetComponent }>直接筛选出component字段完全匹配的路由,比IsEqual靠谱得多,因为它直接对比构造函数类型的字面相等。 - 自动处理联合路由:如果一个组件对应多个路由(比如UserComponent的两个路由),提取出的resolve类型会自动变成联合类型,组件里的
this.data会自动适配所有可能的情况。
可选扩展:合并联合resolve为单一类型
如果你希望把UserComponent的两个resolve类型合并成{ user: typeof resolveUser | typeof resolveNewUser }(而不是两个对象的联合),可以加个小工具类型:
// 把联合类型的resolve合并成单一类型 type MergeResolveUnion<T> = { [Key in keyof T]: T extends { [K in Key]: infer Value } ? Value : never; }; type MergedUserRouteData = MergeResolveUnion<UserRouteData>; // 结果:{ user: typeof resolveUser | typeof resolveNewUser }
内容的提问来源于stack exchange,提问作者PowerKiKi
相关产品推荐
相关产品推荐

