Home > Enterprise >  Generate union type using array type indices
Generate union type using array type indices

Time:11-22

Example:

type DefaultTheme = {
  name: 'default';
  colors: [string, string, string];
};

type CustomTheme = {
  name: 'custom';
  colors: [string, string, string, string, string];
};

type DefaultColor = ???; // 'default-1' | 'default-2' | 'default-3';

type CustomColor = ???; // 'custom-1' | 'custom-2' | 'custom-3' | 'custom-4' | 'custom-5';

The question is whether it's possible to use the length of the colors array to create a mapped union type as in the given example.

The example is simplified for (hopefully) clarity, but in full there is a generic type Theme that takes name and length arguments and uses length to generate a tuple of fixed number of strings.

CodePudding user response:

I simplified @Aleksey L.'s solution a bit and took a different approach:

type Indices<T> = keyof T & `${number}`;
type Indices1<T extends [...any]> = Exclude<Indices<[...T, any]>, '0'>

type N_Test = Indices1<[string, string, string]> // '1' | '2' | '3'
type P_Test = `Test-${N_Test}`; // "Test-1" | "Test-2" | "Test-3"

Main differences:

  • No use of ad-hoc mapped types { [key in ...]: ... }
  • No recursive types (like in @kaja3's answer).
  • No conditional types X extends Y ? ... : ... (Exclude uses this internally, but it is a nice abstraction)

Applied to the example:

type DefaultTheme = {
  name: 'default';
  colors: [string, string, string];
};

type CustomTheme = {
  name: 'custom';
  colors: [string, string, string, string, string];
};

type WithPrefix<T extends {name: string, colors: string[]}> = `${T['name']}-${Indices1<T['colors']>}`;

type DefaultColor = WithPrefix<DefaultTheme>; // 'default-1' | 'default-2' | 'default-3';
type CustomColor = WithPrefix<CustomTheme>; // 'custom-1' | 'custom-2' | 'custom-3' | 'custom-4' | 'custom-5';

Playground

CodePudding user response:

For a non-evil solution, we can use a helper type which converts a tuple like [string, string, string] into a union like 1 | 2 | 3. This can be done recursively, using the length property of the tuple.

type TupleToUnion<T extends any[]> =
    T extends [any, ...infer R] ? T['length'] | TupleToUnion<R> : never

type GenerateUnion<T extends {name: string, colors: string[]}> =
    `${T['name']}-${TupleToUnion<T['colors']>}`

// 'default-3' | 'default-2' | 'default-1'
type DefaultColor = GenerateUnion<DefaultTheme>

// 'custom-5' | 'custom-4' | 'custom-3' | 'custom-2' | 'custom-1'
type CustomColor = GenerateUnion<CustomTheme>

Playground Link

CodePudding user response:

Here's the evil

  • Related