【发布时间】:2021-05-16 14:19:18
【问题描述】:
TypeScript:基于泛型构造一个类型化接口,使用相同的方法,但从所有函数中排除第一个参数。
有一个通用接口,例如:
interface GenericRepository {
insertOne<E>(entity: Type<E>, body: E): Promise<string>;
find<E>(entity: Type<E>, qm: Query<E>): Promise<E[]>;
}
interface Type<T> extends Function {
new (...args: any[]): T;
}
type Query<E> = {
[K in keyof E]: E[K];
};
class User {
firstName?: string;
}
class Company {
name?: string;
}
我希望能够动态“生成”特定的接口/类型,例如:
interface UserRepository {
insertOne(body: User): Promise<string>;
find(qm: Query<User>): Promise<User[]>;
}
interface CompanyRepository {
insertOne(body: Company): Promise<string>;
find(qm: Query<Company>): Promise<Company[]>;
}
以下是我到现在为止的内容,但由于没有正确推断出entity 类型(针对特定接口),因此无法正常工作:
type SpecificRepository<E> = {
[K in keyof GenericRepository]: Fn<GenericRepository[K], E>;
};
type Fn<A extends (...args: any[]) => any, E> = (...a: Params<Parameters<A>>) => ReturnType<A>;
type Params<T extends any[]> = T extends [infer F, ...infer L] ? L : never;
const userRepository = {} as SpecificRepository<User>;
const companyRepository = {} as SpecificRepository<Company>;
如果我这样做(用于测试“生产”类型):
async function someMethod() {
const foundUsers = await userRepository.find({});
}
那么foundUsers 的类型是unknown[],但它应该是User[]。
如何正确地做到这一点?
【问题讨论】:
-
请考虑修改此问题中的代码以构成minimal reproducible example,当将其放入像The TypeScript Playground (link to code) 这样的独立IDE 中时,可以清楚地展示您所面临的问题而不会出现不相关的错误(什么是
Type?什么是Query?什么是User?什么是Company?)。这将使那些想要帮助您的人立即着手解决问题,而无需首先重新创建它。它将使您得到的任何答案都可以针对定义明确的用例进行测试。 -
这里的主要问题是您希望编译器通过以某种方式指定泛型类型参数来自动将泛型函数类型转换为非泛型函数类型;这在类型级别以编程方式是不可能的;你需要降到价值级别,这意味着你需要编写像this这样的冗余代码。有关一般问题,请参阅this question。如果这不能准确描述/解决您的问题,请告诉我。
-
@jcalz 感谢 cmets。我更新了评论以显示完整的代码。我知道这在价值层面是可能的,但我想知道它是否/如何在类型层面上。再次感谢! PD:所以这是不可能的,因为无法从泛型参数(通过类型)获得类型化(泛型)参数,对吧?想知道我是否可以向 TS 团队提交此功能的请求。
-
对,现在不可能。 ms/TS#1213 和 ms/TS#17574 甚至可能是 ms/TS#14466 是相关问题,但我不知道它们中是否有任何一个专门解决“我想从外部在泛型函数中指定泛型类型参数”这种方式。您可能想打开自己的功能请求,但如果它作为其他内容的副本被关闭,请不要感到惊讶。
标签: typescript generics types