Search code examples
typescriptunion-types

How to create a type from an array of objects in TypeScript?


Given a countries array like this:

const countries = [
  { name: "Australia", code: "AU" },
  { name: "Japan", code: "JP" },
  { name: "Norway", code: "NO" },
  { name: "Russian Federation", code: "RU" }
];

What's the easiest way to generate the following type?

type CountryCodes = "" | "AU" | "JP" | "NO" | "RU";

Note: there is an extra empty string.


Solution

  • First, of all, without a slight modification to your input data set type what you want cannot be achieved. As rightfully stated by jonsharpe's comment, the type of array members here is widened to { name: string; code: string; }. This is easily fixed with a as const assertion:

    const countries = [
      { name: "Australia", code: "AU" },
      { name: "Japan", code: "JP" },
      { name: "Norway", code: "NO" },
      { name: "Russian Federation", code: "RU" }
    ] as const;
    

    Now the array itself is considered a tuple, and each member's properties are made readonly as well. After that, you only need a mapped type to extract the tuple's values (usually done with T[number]), get the types of code members and build a union out of them:

    type CountryCodes<T> = { 
      [ P in keyof T ] : T[P] extends { code: string } ? T[P]["code"] : never  
    }[keyof T & number] | "";
    
    

    Where T[P] extends { code: string } constraint ensures we can index T[P] with "code". The result is exactly what you want (note that everything is done purely in type system):

    type cc = CountryCodes<typeof countries>; //type cc = "" | "AU" | "JP" | "NO" | "RU"
    

    Playground


    A more concise version utilizing 4.1's key remapping feature:

    type CountryCodes<T> = keyof { 
      [ P in keyof T as T[P] extends { code: string } ? T[P]["code"] : never ] : T[P]
    } | "";