typescript 基于泛型参数类型键入脚本属性名

aydmsdu9  于 2022-11-18  发布在  TypeScript
关注(0)|答案(1)|浏览(141)

有没有办法在Typescript中创建一个Generic类型,该类型包含一个基于参数类型的属性?
我正在寻找的是类似于Map类型的东西。但是我想使用类型名称,而不是属性名称。
示例用例。我看到很多API返回以下结构

{
  "total": 100,
  "skip": 0,
  "limit": 30,
  "a property named based on the type being queried": [
    // the actual data requested
  ]
}

我想做的是创建一个能够接收这些数据的泛型。

interface ListEnvelope<T> {
    [T as `${T}s`]: T[] // This is not valid Typescript. Its sudocode for what I'd like to do
    total: number
    skip: number
    limit: number
}
interface Product {
    id: number
    title: string
}
const ListEnvelope<Product> = {
  total: 100,
  skip: 0,
  limit: 30,
  Products: [
    // All of my products
  ]
}

这样,我就可以针对API的所有端点进行通用编程。

bwleehnv

bwleehnv1#

在TypeScript中,type names 完全不能被类型系统观察到。(有关更多信息,请参见Is it possible to get a string representation of a type name to use as a template literal type?及其答案。)因此,您无法从名为Product的接口中自动获取字符串文字类型"Product"。如果您想要这样的行为,您必须自己设置它。
例如,您可以将第二个型别参数加入ListEnvelope,它会采用所需的机码名称:

type ListEnvelope<K extends string, T> = { [P in K]: T[] } & {
  total: number
  skip: number
  limit: number
}

您可以这样使用它:

type LEP = ListEnvelope<"Products", Product>;
/* type LEP = { Products: Product[]; } & 
   {  total: number; skip: number; limit: number; } */

const x: ListEnvelope<"Products", Product> = {
  total: 100,
  skip: 0,
  limit: 30,
  Products: [
    // All of my products
  ]
}

或者,您可以预先建立从名称到型别的Map,如下所示:

interface TypeMap {
  Product: Product,
  OtherType: OtherType
  // ... all others
}

然后ListEnvelope将在该Map中查找名称:

type ListEnvelope<T extends TypeMap[keyof TypeMap]> =
  { [K in keyof TypeMap as T extends TypeMap[K] ? `${K}s` : never]: TypeMap[K][] } & {
    total: number
    skip: number
    limit: number
  }

您可以这样使用它:

type LEP = ListEnvelope<Product>;
/* type LEP = { Products: Product[]; } & 
   {  total: number; skip: number; limit: number; } */

const x: ListEnvelope<Product> = {
  total: 100,
  skip: 0,
  limit: 30,
  Products: [
    // All of my products
  ]
}

Playground代码链接

相关问题