Home > Software engineering >  Typescript type for an array that can contain only elements from a specific list
Typescript type for an array that can contain only elements from a specific list

Time:08-15

I want to declare a type for an array that can contain maximum one of the following strings: 'first', 'second', 'third'.

Some valid examples of how that array could be:

  • []
  • [ 'first' ]
  • [ 'first', 'second' ]
  • [ 'first', 'second', 'third' ]
  • [ 'first', 'third' ]
  • [ 'second', 'third' ]

Some invalid arrays:

  • [ 'other-value' ] // no other values should be accepted
  • [ 'first', 'first' ] // no duplicates accepted

The types I've wrote:

export enum MyOptions {
  first = 'first',
  second = 'second',
  third = 'third'
}

export type MyType = {
  name: string;
  email: string;
  listings: {
   MyOptions;
  }[];
};

It has a warning saying Member 'MyOptions' implicitly has an 'any' type, but a better type may be inferred from usage.

So if it is changed to:

export type MyType = {
  name: string;
  email: string;
  listings: {
   options: MyOptions;
  }[];
};

Now, there is no warning but it has that extra options value that I don't think it must be added.

Any ways to fix this?

CodePudding user response:

You can use the Set data structure.

export type Options = 'first' | 'second' | 'third'
export type Listing = Set<Options>

export type MyType = {
    name: string;
    email: string;
    listings: Listing;
};

const myType = {
  name: 'name',
  email: 'email',
  listing: new Set(['first', 'second'])
}

Then you will be sure that there are no repeated options in your listing. Note: actually you can pass repeated options but Set will ignore them.

CodePudding user response:

I think you have an extra set of braces that you don't need.

export type MyType = {
  name: string;
  email: string;
  listings: {
   MyOptions;
  }[];
};

should be changed to

export type MyType = {
  name: string;
  email: string;
  listings: MyOptions[];
};

You also have a more concise option: use string literal types:

export type MyType = {
  name: string;
  email: string;
  listings: ('first' | 'second' | 'third')[];
};

CodePudding user response:

It should be like this:

export type MyType = {
    name: string;
    email: string;
    listings: MyOptions[];
};

Let's assume you have variable named x of MyType, then to push new value to listings member you can do x.listings.push(MyOptions.first)

CodePudding user response:

The second solution just means that you now have an object that could look like:

const exampleObject = {
  name: 'Ralf';
  email: '[email protected]';
  listings: {
   options: 'first';
  }[];
};

Which is not what you intend, because you can´t have a ['first', 'second'] there, but only ONE value.

If you use the style

listings: MyOptions[];

Then it allows also the content ['first', 'first'].

As far as i know, there is no way to limit the possible values not only by its content, but also by its amount (having at maximum one of each values) via typescript types.

So you would have to create your own logic.

For example having a small business object

class Options {
  private _values: MyOptions[] = [];

  addOption(option:MyOption){
    if(this._values.some(option)){
      return; // or throw an error, depends on your usecase
    }
    this.values.concat(option);
  }

  get options():MyOptions{
    return this._values;
  }
}

Now you can have your wrapper type:

export type MyType = {
  name: string;
  email: string;
  listings: Options
};
  • Related