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
};
Full example on playground
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".
We required T to extend an array, so we can set never after : 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);
Full example on playground
const result: Type = {a: false, b: false}?