如果你继续做你的类型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