HardCoreQual
HardCoreQual

Reputation: 391

convert array of strings to object keys in typescript

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

Answers (3)

Matt K
Matt K

Reputation: 4948

You can do this without a generic now.

type Type = {
  [K in typeof arrayData[number]]: boolean
}

Upvotes: 2

Mahdi Aryayi
Mahdi Aryayi

Reputation: 1120

I found 2 issues in your sample:

  1. the dynamic key for an object should be inside brackets: (key: should be [key]:

  2. 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

Luiz Ferraz
Luiz Ferraz

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
};

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

Upvotes: 20

Related Questions