yaforsh
yaforsh

Reputation: 55

How to create a union type from nested arrays?

I have a constant object like this

const rack = {
    topShelf: {
        colors: ["red", "green"],
    },
    middleShelf: {
        colors: ["yellow", "blue"],
    },
    bottomShelf: {
        colors: ["orange", "purple"],
    },
}

And I want to create a union type like:

type Color = "red" | "green" | "yellow" | "blue" | "orange" | "purple"

The rack object is just an example. In a real application, the object has around 30 fields.

I know how to do it manually

type Color = 
    | typeof rack["topShelf"]["colors"][number] 
    | typeof rack["middleShelf"]["colors"][number]
    | typeof rack["bottomShelf"]["colors"][number]

But it is error-prone and I'm sure there is a way to get this union type inferred by Typescript.

Upvotes: 2

Views: 294

Answers (2)

yaforsh
yaforsh

Reputation: 55

I have ended up with this solution:

const rack = {
    topShelf: {
        colors: ["red", "green"],
    },
    middleShelf: {
        colors: ["yellow", "blue"],
    },
    bottomShelf: {
        colors: ["orange", "purple"],
    },
} as const

const colors = Object.values(rack).map(shelf => shelf.colors).flat()

type Color = typeof colors[number]

I will need "colors" as a runtime value later so in my specific case this solution is the best one.

But the solution from @captain-yossarian is generic so I marked it as an answer.

Upvotes: 0

Yes, it is possible.

const rack = {
  topShelf: {
    colors: ["red", "green"],
  },
  middleShelf: {
    colors: ["yellow", "blue"],
  },
  bottomShelf: {
    colors: ["orange", "purple"],
  },
  extraDeepNestedProperty: {
    nestedProperty: {
      colors: ['rgb']
    }
  }
} as const

type Rack = typeof rack

type IsNever<T> = [T] extends [never] ? true : false

type Union<T, Cache extends string = never> =
  IsNever<keyof T> extends true
  ? Cache
  : {
    [Prop in keyof T]:

    T[Prop] extends ReadonlyArray<string>
    ? Cache | T[Prop][number]
    : Union<T[Prop], Cache>
  }[keyof T]

type Result = Union<Rack>

Treat Cache as a memoization.

Union is a recursive type.

Playground I made generic solution which works not only with 2 levels of nesting,

Upvotes: 3

Related Questions