将一个对象结构的高级 TypeScript 推断到另一个对象结构
情景
我有一个对象,它定义了一些需要解析的数据的配置。该对象包含类以及这些类上存在的方法的字符串名称
{
orders: {
service: OrderService, // The class
serviceMethod: 'getOrders' // The method name which exists on the class
},
cart: {
service: OrderService,
serviceMethod: 'getCart'
}
}
这个类看起来像这样
export class OrderService {
public getOrders = (): Observable<Order[]> = {
return of([ {id: '1', name: 'Order 1' ])
};
public getCart = (): Observable<Cart> => {
return of({ products: [] })
};
}
一旦配置被执行,解析后的数据如下所示:
{
orders: [ {id: '1', name: 'Order 1' ], // Data of type Order[]
cart: { products: [] } // Data of type Cart
}
问题
我正在尝试根据解析器配置自动推断已解析数据的类型。我有这个类型,但即使 T[P]['serviceMethod'] extends keyof T[P]['service'] 应该确保 T[P]['serviceMethod'] 是提供类的一个键,它仍然无法识别它
这是该问题的 StackBlitz 链接 https://stackblitz.com/edit/stackblitz-starters-9yqbuq?file=src%2Fapp%2Fapp.interface.ts
如果有人能修好这个,我会非常感激 :)
export interface ResolverResource<TService = any> {
service: Type<TService>;
serviceMethod: keyof TService;
}
export type ResolvedData<T, TService = any> = {
// Check if T[P] is a ResolverResource
[P in keyof T]: T[P] extends ResolverResource<TService>
// Check if T[P]['serviceMethod'] exists on T[P]['service']
? T[P]['serviceMethod'] extends keyof T[P]['service']
// Check if T[P]['service'][T[P]['serviceMethod']] is a function that returns an Observable
// and infer the output type of the Observable
? T[P]['service'][T[P]['serviceMethod']] extends (...args: any[]) => Observable<infer U>
? U
: never
: never // Here is where it fails. It doesn't recognize that T[P]['serviceMethod'] is a key of T[P]['service']
: never;
};
答案#1
开始觉得在 TypeScript 中使用 keyof 时,从 Observable 推断出显式类型存在限制。文档暗示它总是会输出所有键的联合类型。
执行以下操作可以让你的代码编译,但 orders 和 carts 属性都会互相联合。
export type ResolvedData<T, TService = any> = {
[P in keyof T]: T[P] extends ResolverResource<TService>
?
// You were missing this part, as 'keyof' Type<T> doesnt bring back the keys in the generic constraint
T[P]['service'] extends Type<infer U>
?
T[P]['serviceMethod'] extends keyof U
?
U[T[P]['serviceMethod']] extends (
...args: any[]
) => Observable<infer V>
? V
: never
: never
: never
: never;
};
这会给你类似这样的东西:
const resolvedData: ResolvedData<typeof resourceConfig> = {
orders: [],
cart: { products: [] },
};
// resolvedData.orders == Cart | Order[]
// resolvedData.cart == Cart | Order[]
