我有一个下一个代码:
const arr = [
{
name: 'john',
age: '20'
},
{
name: 'anna',
age: '30'
}
]
现在我基于arr:
创建一个新对象const obj = {} // {john:20,anna:30}
arr.forEach(o => obj[o.name]=o.age)
如何动态类型这个对象,需要这样的接口:
interface IObj {
john: number,
anna: number
}
您可以直接推断所有键和值,如下所示:
const arr = [
{
name: 'john',
age: '20'
},
{
name: 'anna',
age: '30'
}
]
type Elem = {
name: string;
age: `${number}`
}
type Callback<Item> =
(Item extends { name: infer Name, age: infer Age }
? (Name extends PropertyKey
? Record<Name, Age>
: never)
: never
)
type Reduce<List extends ReadonlyArray<any>, Acc extends Record<string, `${number}`> = {}> =
(List extends []
? Acc
: (List extends [infer Head, ...infer Tail]
? Reduce<Tail, Acc & Callback<Head>>
: never)
)
const builder = <
Name extends string,
Age extends `${number}`,
Item extends { name: Name, age: Age },
List extends ReadonlyArray<Item>
>(list: [...List]) =>
list.reduce((acc, elem) => ({
...acc,
[elem.name]: elem.age
}), {} as Reduce<List>)
const result = builder([
{
name: 'john',
age: '20'
},
{
name: 'anna',
age: '30'
}
])
result.anna // 30
result.john // 20
游乐场
我使用了几个额外的泛型,Name
,Age
,Item
和List
来推断每个属性。把它看作是一种解构。如果你想在TypeScript中推断出一些嵌套的属性,你应该为它创建generic。
Reduce
-是实用程序类型。它递归地迭代参数,并折叠每个元素。就像你在实现中做的那样。除了,你用的是forEach
而我用的是reduce
。这只是个人喜好的问题。
如果你认为上面的版本是一个开销或过度工程,你可以使用这个:
type Elem = {
name: string;
age: `${number}`;
}
const builder = <
List extends ReadonlyArray<Elem>
>(list: List) =>
list.reduce((acc, elem) => ({
...acc,
[elem.name]: elem.age
}), {} as Record<string, `${number}`>)
const result = builder([
{
name: 'john',
age: '20'
},
{
name: 'anna',
age: '30'
}
])
result.anna // 30
result.john // 20