Mladen
Mladen

Reputation: 2210

How to declare an array of exactly one alias declared in a type alias

If I have a TypeScript type alias defined like this:

export type PlatformType = 'em' | 'ea' | 'hi';

how can I declare a property in an interface so that that property is an array of exactly one type of each PlatformType alias? For example, I want this:

export interface Constants {
  platformTypes: PlatformType[]; // how to declare a type for platformTypes?
}

so that when I have an object of Constants type, I need to have this:

export const constants: Constants = {
  platformTypes: ['em', 'ea', 'hi']
}

Basically, I would like to get an error if I have this:

export const constants: Constants = {
  platformTypes: ['em', 'ea'] // should error, missing 'hi'
}

or

export const constants: Constants = {
  // this will error anyway because 'extra' doesn't belong to PlatformType alias
  platformTypes: ['em', 'ea', 'hi', 'extra']
}

or

export const constants: Constants = {
  platformTypes: ['em', 'em', 'ea', 'hi'] // should error, duplicate 'em'
}

Upvotes: 4

Views: 445

Answers (1)

I think you should define a permutation of all allowed tuples.

For instance, if you have a union of three elements you need to create at least 6 tuples:

["em", "ea", "hi"] | ["em", "hi", "ea"] | ["ea", "em", "hi"] | ["ea", "hi", "em"] | ["hi", "em", "ea"] | ["hi", "ea", "em"]

This is because union is unordered data structure.

export type PlatformType = 'em' | 'ea' | 'hi';


// credits goes to https://twitter.com/WrocTypeScript/status/1306296710407352321
type TupleUnion<U extends string, R extends any[] = []> = {
    [S in U]: Exclude<U, S> extends never ? [...R, S] : TupleUnion<Exclude<U, S>, [...R, S]>;
}[U];

export interface Constants {
  platformTypes: TupleUnion<PlatformType>; // how to declare a type for platformTypes?
}

export const constants: Constants = {
  platformTypes: ['em', 'ea', 'hi'] // ok
}

export const constants2: Constants = {
  platformTypes: ['em', 'ea'] // error
}

export const constants3: Constants = {
  platformTypes: ['em', 'ea', 'hi', 'extra'] // error
}

export const constants4: Constants = {
  platformTypes: ['em', 'em', 'ea', 'hi'] // error
}

Playground

Please keep in mind, that order of unions in typescript is tricky. Please see this question and this issue

You can find more examples in my blog

Upvotes: 1

Related Questions