【问题标题】:TypeScript: Construct a typed interface based on a generic one [duplicate]TypeScript:基于泛型构造一个类型化接口[重复]
【发布时间】: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#1213ms/TS#17574 甚至可能是 ms/TS#14466 是相关问题,但我不知道它们中是否有任何一个专门解决“我想从外部在泛型函数中指定泛型类型参数”这种方式。您可能想打开自己的功能请求,但如果它作为其他内容的副本被关闭,请不要感到惊讶。

标签: typescript generics types


【解决方案1】:

你快到了!为了帮助 TypeScript 的推理机制,您应该使 interface 通用,而不仅仅是它的成员函数。这样你也不需要子类化:

interface GenericRepository<E> {
  insertOne(entity: Type<E>, body: E): Promise<string>;

  find(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;
}

const userRepository = {} as GenericRepository<User>;
const companyRepository = {} as GenericRepository<Company>;

async function someMethod() {
  const foundUsers = await userRepository.find({});
}

【讨论】:

  • 这行不通,因为findinsertOne 仍然需要entity 参数(这是我要删除的参数)。请注意,要点是:GenericRepository 接口确实已经存在,我想从它自动获取一个类型化的(通过类型/接口)存储库接口,但不包括方法中的实体参数。谢谢。
猜你喜欢
  • 2021-10-29
  • 2021-03-26
  • 2020-03-22
  • 2018-07-23
  • 1970-01-01
  • 2020-12-05
  • 2019-04-06
  • 1970-01-01
  • 2018-09-15
相关资源
最近更新 更多