这实际上并不难。您所需要的只是一个映射类型,其键重新映射到元组成员的 name 属性的类型(可以很好地使用 key remapping since 4.1 完成)和相应成员的 type 属性的值。
首先,您需要让编译器知道properties 数组实际上是一个带有as const 断言的元组:
const properties = [
{ name: 'name', type: '' },
{ name: 'age', type: 0 },
{ name: 'sex', type: ['m', 'f'] as const },
{ name: 'username', type: '' }
] as const;
接下来,让我们通过定义一个能够对元组成员的任意属性进行操作的泛型类型来使该类型可重用。首先,我们需要提取索引以便能够映射
成员与属性一对一。这是通过Exclude<keyof T, keyof readonly any[]> 完成的(仅保留索引)。
我们可以使用生成的索引联合来进行映射:
type TupleToProps<T extends readonly any[], VP extends keyof T[number], VV extends keyof T[number]> = {
[ P in Exclude<keyof T, keyof readonly any[]> as T[P][VP] & string ] : T[P][VV]
};
VP 和 VV 确保实用程序类型可以与任何同质的对象元组一起使用,& string 只留下与字符串兼容的属性。这已经给我们带来了非常好的结果:
type PersonTest = TupleToProps<typeof properties, "name", "type">;
/**
* type PersonTest = {
* name: "";
* age: 0;
* sex: readonly ["m", "f"];
* username: "";
* }
*/
在那之后,这只是表面上的改变:从类似元组的类型中提取值并扩大文字类型(由this 和this Q&A 提供,小幅升级以排除不应扩大的键) :
type ToPrimitive<T> =
T extends string ? string
: T extends number ? number
: T extends boolean ? boolean
: T;
// mapped types which will preserve keys with more wide value types
type Widen<O, E = never> = {
[K in keyof O]: K extends E ? O[K] : ToPrimitive<O[K]>
}
type TupleToPropsTwo<T extends readonly any[], VP extends keyof T[number], VV extends keyof T[number], E extends T[number][VP]> = Widen<{
[ P in Exclude<keyof T, keyof readonly any[]> as T[P][VP] & string ] : T[P][VV] extends readonly any[] ? T[P][VV][number] : T[P][VV]
}, E>;
type PersonTest2 = TupleToPropsTwo<typeof properties, "name", "type", "sex">;
/**
* type PersonTest2 = {
* name: string;
* age: number;
* sex: "m" | "f";
* username: string;
* }
*/
Playground