i have a next code:
const arr = [
{
name: 'john',
age: '20'
},
{
name: 'anna',
age: '30'
}
]
Now i create a new object based at arr:
const obj = {} // {john:20,anna:30}
arr.forEach(o => obj[o.name]=o.age)
How can i dynamical type this obj, need interface like this:
interface IObj {
john: number,
anna: number
}
CodePudding user response:
You can literaly infer all keys and values like here:
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
I have used several extra generics, Name
, Age
, Item
and List
to infer each property. Think about it as about destructure. If you want to infer some nested prop in TypeScript, you should create for it generic.
Reduce
- is an utility type. It recursively iterates over the argument, and folds each element. Just like you did in your implementation. Except, you used forEach
whereas I have used reduce
. It just a matter of preference.
If you think above version is an overhead or overengineering, you can use this:
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