如何在 TypeScript 中定义通用对象数组,每个项目都有不同的模板参数[重复]

2024-04-15

我试图为表单定义一个字段数组,其中每个项目可能有不同的类型。我定义了这些类型:

  interface FormData{
    value1:number
    value2:number|null
    value3:string
    value4:boolean
  }

  interface Field<T,K extends keyof T,TV=T[K]>{
    key:K&string
    value:TV
    validation?:(value:TV)=>boolean
  }

定义一个单独的字段效果很好:

  const field:Field<FormData,'value1'>={
    key:'value1',
    value:1,
    validation(value:number):boolean{
      return value<3
    }
  }

但是当为多个项目定义一个数组时,如下所示:

  const fields:Field<FormData,keyof FormData>[]=[
    {
      key:'value1',
      value:1,
      validation(value:number):boolean{
        return value<3
      }
    },
    {
      key:'value3',
      value:'xxx',
      validation(value:string):boolean{
        return value!=='xxx'
      }
    }
  ]

打字稿是:

  1. 允许value可以是任何类型,只要它是number|null|string|undefined(例如一个字符串value1)
  2. 抱怨validation()函数,因为它们不接受number|null|string|undefined作为参数

有没有办法帮助 TS 推断数组中每个项目的正确类型?理想情况下,我还想将数组定义为Field<FormData>[].

我正在使用 TypeScript 4.3。


如果你继续做你的类型Field<T, K> generic https://www.typescriptlang.org/docs/handbook/2/generics.html在钥匙里K,那么你确实会发现表示数组很烦人Field<T, ??>对于不同的键K。有一些方法可以做到这一点映射元组 https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-1.html#mapped-types-on-tuples-and-arrays,但实际上,这需要您指定或让编译器推断特定的键类型数组。但你不关心which键都用了,只要都是some实际的钥匙。

这是一个典型的用例存在量化的泛型 https://en.wikipedia.org/wiki/Type_system#Existential_types,TypeScript 不直接支持。 (有一个这样的请求微软/TypeScript#14466 https://github.com/microsoft/TypeScript/issues/14466,但目前这是一个缺失的功能。)TypeScript 的泛型与大多数语言的泛型一样,是普遍量化,你可以将其视为无限路口 https://www.typescriptlang.org/docs/handbook/2/objects.html#intersection-types的类型。另一方面,存在量化的泛型充当无限union https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types的类型。

但是等等,你的FormData与大多数对象类型一样,接口有一个finite键列表。你不需要无限的联合;你可以只使用工会!意思是,将你原来的并集起来Field<FormData, K>类型,对于每个K in keyof FormData。这是定义它的一种方法:

  type Field<T> = { [K in Extract<keyof T, string>]-?: {
    key: K
    value: T[K],
    validation?: (value: T[K]) => boolean
  } }[Extract<keyof T, string>];

这构建了一个映射类型 https://www.typescriptlang.org/docs/handbook/2/mapped-types.html为每个键指定您想要的字段类型K in keyof T,然后立即indexes https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html进入该映射类型以获得其属性的联合。

您可以看到它生成了您需要的类型:

  type FieldFormData = Field<FormData>;

  /* type FieldFormData = {
    key: "value1";
    value: number;
    validation?: ((value: number) => boolean) | undefined;
} | {
    key: "value2";
    value: number | null;
    validation?: ((value: number | null) => boolean) | undefined;
} | {
    key: "value3";
    value: string;
    validation?: ((value: string) => boolean) | undefined;
} | {
    ...;
} */

而且现在还没有明确的规定K类型中提到的,你可以使用Array<Field<FormData>>:

const fields: Field<FormData>[] = [
  {
    key: 'value1',
    value: 1,
    validation(value: number): boolean {
      return value < 3
    }
  },
  {
    key: 'value3',
    value: 'xxx',
    validation(value: string): boolean {
      return value !== 'xxx'
    }
  }
]

它也会捕获错误:

const badFields: Field<FormData>[] = [
  { key: "value2", value: "oops" }, // error!
  { key: "value4", value: true } // okay
]

Playground 代码链接 https://www.typescriptlang.org/play?#code/HYQwtgpgzgDiDGEAEApeIA2AvJBvAUPkkqJLAsgGIAiehxxAlsAC4QBOAZhUpQPbsw1ECxB0GDAG6YArhACMALhIywAIw5EJSaRjkAmZcFUb2SAD4qMGLRN1yAzMqgt2zAOa2psiABZlanx8GBAgwF4AvvQMLACeMFSMEBgAJgA8ACoAfEgAvHhIANoA0kjMSACiAB6uCCxpANYQsXycSBkANEgubsDuWQC6ALQA-MoE2khNscrFXsT2EMoZJQMd8zqYjCkijHzAY0gAFIvLqwCUeTmBwaHhEhFIEYXVtfD1063tXT0eg9HEOIJXhJVL8QTCUR5EHJdLgoQiEBZAFIAD0ACokEDErD4ZCxPkJtppsoAESLeSkgDcG1OKnUHBpk10212+0ORxOPiMJg4l1y1yCITCl0sMmAKQgnGYEBSTKeFnExOaZMW+mptO59NMiuM1nldi2OxYewOyk5dOMDLMlj1GH5gtuIsV4sl0uAsvlj0sRIkJKQ5J8Dg1zK1vz6Bu8GFZJvZ5q5eiW3VcHgdSBuwuAoqQrqlMrlWm9SokADoy16kOjUSj4PsXEhpbDlJRQXCBAjRDlCRt-QByCm99ahxPKeRD7Qs42mhNyHnW84BIV3YvadgQFgydjATaJpBpJAODZRB41ussButqDN1tpPGIrKFAbQwobX3KmZIfs+eSDjYLLVjn+O4xtOlq8uwC7pkuYQrpMa4bluO5yHuB5Ace2gROOEhvn6KqfosDi-pM-4jp+VTkURxGTmywAzkm4buJBGbLjhq7rpu26LEgACEuT5L25FVL2aFHl4AynsA9ZqCAKQtrCV4wqkt7tviD5PvkL6TLgUx4YGibql0dKkkEMBQKSTxdKiqJIBw7ACNxr46R+elyL4pKGVqrjIY8VlIHwDQgLEYnRFE+AREAA

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 TypeScript 中定义通用对象数组,每个项目都有不同的模板参数[重复] 的相关文章

随机推荐