简体   繁体   English

以类型为参数的通用 TypeScript API

[英]Generic TypeScript API with types as parameters

Let's say I have two functions like this (I currently have like 20 different that looks pretty much the same. Only difference is the route and the DTOs) I don't want to send both route and DTO in the function call as that would make the function calls more messy/complicated.假设我有两个这样的函数(我目前有 20 个看起来几乎相同的不同函数。唯一的区别是路由和 DTO)我不想在函数调用中同时发送路由和 DTO,因为那样会使该函数调用更混乱/复杂。 So I'd rather only call it with either Asset |所以我宁愿只用 Asset | 调用它。 Personnel |人事 | Something else that is valid其他有效的东西

 async getAllAssets() {
    return await this.get<AssetDto[]>('assets/assets')
  }

 async getAllPersonnels() {
    return await this.get<PersonnelDto[]>('personnel/personnels')
  }

And I want to make it more generic so I only need one function instead of two.我想让它更通用,所以我只需要一个函数而不是两个。 How do I implement that?我该如何实施? My own try is below.我自己的尝试如下。 Maybe it will make it more clear what I actually want as well.也许它会让我更清楚我真正想要什么。 I'm a total newbie with TypeScript and only been at it for one week.我是 TypeScript 的新手,只用了一周。 My "dream" implementation would also include enum so I could call the function with eg Entity.Asset or Entity.Personnel and then under the hood it then knows that it should use the the route and dto for either Asset or Personnel.我的“梦想”实现还将包括枚举,因此我可以使用例如 Entity.Asset 或 Entity.Personnel 调用该函数,然后在引擎盖下它知道它应该为 Asset 或 Personnel 使用路由和 dto。

export type Asset = {
    route: '/assets/asset',
    dto: AssetDto[]
}

export type Personnel = {
    route: '/personnel/personnel',
    dto: PersonnelDto[]
}

export type Entity = Asset | Personnel

And here is the example of a more generic function:这是一个更通用的函数的例子:

 async getAll<T extends Entity>(entity: T) {
    return await this.get<typeof entity.Dto>(entity.route)
  }

But I don't know how to actually call the function with a type?但我不知道如何用类型实际调用函数? Or is it even possible to do it like this?或者甚至可以这样做吗?

  async howIWantAFunctionCallToBeLike() {
    await this.getAll(Entity.Asset))
  }

As Tobias has said, you need something to survive to runtime to pass to your generic function.正如 Tobias 所说,你需要一些东西来生存到运行时才能传递给你的泛型函数。 I recommend using a simple generic class:我建议使用一个简单的泛型类:

class Route<T> {
  constructor(readonly path: string) {
  }

  transform(data: unknown): T[] {
    // unsafe cast by default; subclasses could do something more
    return data as T[];
  }
}

const personnelRoute = new Route<PersonnelDto>('/personnel/personnel');

async function getAll<T>(route: Route<T>): Promise<T[]> {
  return route.transform(await doFetch(route.path));
}

It is possible to utilize generics here to infer the return type based on something passed into the function.可以在这里使用泛型来根据传递给函数的内容来推断返回类型。

You could create an interface like this:您可以创建这样的界面:

interface Entities {
  '/assets/asset': AssetDto[],
  '/personnel/personnel': PersonnelDto[]
} 

With this interface, we can create a generic function which returns the correct type based on the passed route.使用这个接口,我们可以创建一个泛型函数,它根据传递的路由返回正确的类型。

async getGeneric<T extends keyof Entities>(route: T){
  return await this.get<Entities[T]>(route)
} 

async otherFn() {
  const a = await this.getGeneric('/assets/asset')
  //    ^? AssetDto[]
  const b = await this.getGeneric('/personnel/personnel')
  //    ^? PersonnelDto[]
}

Playground 操场


Or use an enum:或使用枚举:

enum Routes {
  Asset = '/assets/asset',
  Personnel= '/personnel/personnel'
}

interface Entities {
  [Routes.Asset]: AssetDto[],
  [Routes.Personnel]: PersonnelDto[]
} 
async getGeneric<T extends keyof Entities>(route: T){
  return await this.get<Entities[T]>(route)
} 

async howIWantAFunctionCallToBeLike() {
  const a = await this.getGeneric(Routes.Asset)
  //    ^? AssetDto[]
  const b = await this.getGeneric(Routes.Personnel)
  //    ^? PersonnelDto[]
  }

Playground操场

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM