将数组映射到接口
Map array to an interface
假设我有一个如下所示的数组:
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);
where manipulate 是我希望发现的一些神奇的 TS 功能。
我相信这是一个很好的起点:
https://blog.mariusschulz.com/2017/01/20/typescript-2-1-mapped-types
但很难弄清楚。
是的,您可以这样做,但它需要两种 mapped and 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 }>
表示"find the element of T
that corresponds to the name 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
,这将是您所期望的。
好的,希望对您有所帮助。祝你好运!
假设我有一个如下所示的数组:
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);
where manipulate 是我希望发现的一些神奇的 TS 功能。
我相信这是一个很好的起点: https://blog.mariusschulz.com/2017/01/20/typescript-2-1-mapped-types
但很难弄清楚。
是的,您可以这样做,但它需要两种 mapped and 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 }>
表示"find the element of T
that corresponds to the name 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
,这将是您所期望的。
好的,希望对您有所帮助。祝你好运!