是的,你可以这样做,但它需要mapped 和conditional 类型。
首先,您需要一个类型来表示您从 "boolean" 这样的类型名称到 boolean 这样的实际类型的映射。
type TypeMapping = {
boolean: boolean,
string: string,
number: number,
// any other types
}
然后您需要一个辅助函数来确保您的options 值不会将name 和type 属性的类型扩展到string。 (如果您检查您的options 值,它的类型类似于{name: string, type: string}[],它已经丢失了您想要的特定name 和type 值。)您可以使用generic constraints 来执行此操作,如下所示:
const asOptions = <K extends keyof any,
T extends Array<{ name: K, type: keyof TypeMapping }>>(t: T) => t;
让我们看看它是否有效:
const options = asOptions([
{
name: 'foo',
type: 'boolean'
},
{
name: 'bar',
type: 'string'
},
{
name: 'bar',
type: 'number'
}
]);
如果您检查一下,您会发现它现在是一个类型数组,其中 name 和 type 中的每一个都缩小为文字 "foo"、"bar"、"number" 等。
最后我们要做你想要的manipulate类型函数。我就叫它OptionsToType:
type OptionsToType<T extends Array<{ name: keyof any, type: keyof TypeMapping }>>
= { [K in T[number]['name']]: TypeMapping[Extract<T[number], { name: K }>['type']] }
这可能看起来很复杂。让我们看看我是否可以分解它。
T extends Array<{ name: keyof any, type: keyof TypeMapping }>
表示T 必须是一个对象数组,其中name 字段类似于对象键,type 字段类似于上述TypeMapping 类型的键。
= { [K in T[number]['name']]: ... }
从T 数组的每个元素中遍历name 属性中的所有键名
Extract<T[number], { name: K }>
意思是“找到T中与名称K对应的元素”...
Extract<T[number], { name: K }>['type']
...并查找其'type' 属性...
TypeMapping[Extract<T[number], { name: K }>['type']]
...并将其用作TypeMapping 类型的索引。
好的,让我们看看它是否有效:
export type Opts = OptionsToType<typeof options>;
如果您检查Opts,您会看到:
{
foo: boolean;
bar: string | number;
}
正如你所料--- 呃,等等,为什么bar 类型的属性是string | number?哦,因为您将bar 放入options 两次。将第二个更改为baz,这将是您所期望的。
好的,希望对您有所帮助。祝你好运!