godblessstrawberry
godblessstrawberry

Reputation: 5058

How do I create type for Array of enum values with mandatory members in typescript?

How do I create type / interface (AnyColorsWithBlackAndWhite in example below) for an array of enum values with mandatory members? I need black AND white to be always present. I tried to intersect, omit and extend both Array<Color> and Array<Color.black | Color.white> at the same time but no luck so far:

enum Color {
  red,
  green,
  yellow,
  blue,
  black,
  white,
}

interface AnyColorsWithBlackAndWhite
  extends Array<Color | Color.black | Color.white> {}

export const Correct: AnyColorsWithBlackAndWhite = [Color.red, Color.yellow, Color.black, Color.white];

export const Wrong1: AnyColorsWithBlackAndWhite = [Color.red, Color.yellow];

export const Wrong2: AnyColorsWithBlackAndWhite = [Color.red, Color.yellow, Color.black];

Upvotes: 2

Views: 59

Answers (1)

I see two ways, you either create function for inference and validation or you create a union of all allowed states. First approach:

enum Color {
  red,
  green,
  yellow,
  blue,
  black,
  white,
}

type RequiredElements = Color.black | Color.white

const withRequired = <
  Item extends Color,
  List extends Item[]
>(list: RequiredElements[] extends [...List] ? List : never) => list

const ok = withRequired([Color.red, Color.yellow, Color.black, Color.white]) // ok
const error = withRequired([Color.red, Color.yellow, Color.black]) // error

enter link description here

Second:

enum Color {
  red,
  green,
  yellow,
  blue,
  black,
  white,
}

// https://github.com/microsoft/TypeScript/issues/13298#issuecomment-692864087
type TupleUnion<U extends string | number, R extends any[] = []> = {
  [S in U]: Exclude<U, S> extends never
  ? S extends Color.black | Color.white
  ? [...R, S] : [...R, S?] : TupleUnion<Exclude<U, S>,
    S extends Color.black | Color.white ? [...R, S]
    : [...R, S?]
  >;
}[U];

type Ok = TupleUnion<Color>

export const Correct: Ok = [Color.red, Color.yellow, Color.black, Color.white]; // ok
export const COrrect1: Ok = [Color.red, Color.white, Color.yellow, Color.black]; // ok

export const Wrong1: Ok = [Color.red, Color.yellow]; // error

export const Wrong2: Ok = [Color.red, Color.green, Color.black]; // error

Playground

As you might have noticed, I have created all possible states of this array. It is a lot: 6! = 6 * 5 * 4 * 3 * 2 * 1

TupleUnion generates a combination of all allowed states and makes every non black and white color as optional

Upvotes: 1

Related Questions