Reputation: 391
I have next array
const arrayData = ['a', 'b'] as const;
this array have next type ArrayType = ('a' | 'b')[];
I want obtain object what coincide with next type:
type Type = {
a: boolean,
b: boolean,
}
expected object
const result : Type = {
a: false,
b: false,
}
I want transform arrayData
to result
const result: Type = arrayData.reduce((r, key) => {
return {
...r,
key: false,
}
}, {});
but this code not have valid types
Upvotes: 9
Views: 12928
Reputation: 4948
You can do this without a generic now.
type Type = {
[K in typeof arrayData[number]]: boolean
}
Upvotes: 2
Reputation: 1120
I found 2 issues in your sample:
the dynamic key for an object should be inside brackets: (key:
should be [key]:
you can define the type of initial reduce
accumulator by as
operator in TypeScript.
const arrayData = ['a', 'b'] as const;
type Type = {
a: boolean,
b: boolean,
}
const result: Type = arrayData.reduce((r, key: string) => {
return {
...r,
[key]: false,
}
}, {} as Type);
Upvotes: 0
Reputation: 1525
Your first assumptions is wrong:
const foo = ['a', 'b'] as const; // Type is ['a', 'b'], a tuple of literals
const bar = ['a', 'b']; // Type is ('a' | 'b')[], a list of a union of literals
Using the ['a', 'b']
regardless of the const
you can type it like this:
type ObjectFromList<T extends ReadonlyArray<string>, V = string> = {
[K in (T extends ReadonlyArray<infer U> ? U : never)]: V
};
Explaining:
T extends ReadonlyArray<string>
require a type parameter that is an array with elements that can become strings, being Readonly allows you to use const
. Every string literal type can be passed into a string.T extends ReadonlyArray<infer U> ? U : never
is a conditional that means: "if T is an array, get the type of its elements (U
), otherwise no type".:
meaning we know it will never happen.
infer U
will infer the smallest set of types possible for the T, which in your case is the union of the string literals ('a' | 'b'
)[K in ...]: V
uses all the possible values in the element of the array as keys pointing to values of type V
(which we set the default to string
, but you can pass another one).Lastly, about the reduce. You need to set the type that you are working with during the iterations:
const result: Type = arrayData.reduce((r, key) => {
return {
...r,
[key]: false,
}
}, {} as Type); // Here
To restrict the type exactly you need to type both the reduce, by typing the initial accumulator, and the return of the function:
const result: Type = arrayData.reduce((r, key): Type /* HERE */ => {
return {
...r,
[key]: false,
}
}, {} as Type);
Upvotes: 20