说我有一个看起来像这样的数组:
const options = [
{
name: 'foo',
type: 'boolean'
},
{
name: 'bar',
type: 'string'
},
{
name: 'bar', // should be baz not bar
type: 'number'
}
]
我希望将此数组用作接口,如下所示:
export interface Opts {
foo: boolean,
bar: string,
baz: number
}
所以可能必须是这样的:
export type Opts = manipulate(typeof options);
操纵是我希望发现的一些神奇的TS功能。
我认为这是一个不错的起点: https://blog.mariusschulz.com/2017/01/20/typescript-2-1-mapped-types
但是很难弄清楚。
答案 0 :(得分:6)
是的,您可以执行此操作,但是它同时需要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
值的跟踪。)可以使用{ {3}}为此,如下:
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"
,{ {1}},等等。
最后,我们必须执行所需的"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
类型的键)。< / p>
TypeMapping
从 = { [K in T[number]['name']]: ... }
数组的每个元素中遍历name
属性中的所有键名
T
表示“找到与名称 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
,这将是您期望的。
好的,希望能有所帮助。祝你好运!