pea3nut
pea3nut

Reputation: 2164

TypeScript - How can omit some items from an Enum in TypeScript?

I defined an Enum to clarify APIs request status:

const enum Errcode{
    Ok=0,
    Error=1,
    AccessDeny=201,
    PostsNotFound=202,
    TagNotFound=203,
    //...
}

type SuccessErrcode =Errcode.Ok;
type NotFoundError =Errcode.PostsNotFound|Errcode.TagNotFound;
type ErrorErrcode=/* there */;

How can I define the ErrorErrcode that means all items of Errcode except Errcode.Ok (and it should include all items of NotFoundError)?

I can't define the more granular types and Union them likes this:

enum SuccessErrcode {
    Ok =0,
}
enum NotFoundErrcode {
    PostsNotFound=202,
    TagNotFound=203,
}
enum ErrorErrcode {
    Error=1,
}
type Errcode =SuccessErrcode|NotFoundError|SuccessErrcode;

If I do this, I will can't use Errcode.xxx - for use a code, I must to know where it be assigned of.(e.g. from Errcode.TagNotFound to NotFoundError.TagNotFound). And consider that - when there have TagErrcode and NotFoundErrcode, the TagNotFound=203 will be defined twice.

Upvotes: 143

Views: 100652

Answers (5)

KasunSH
KasunSH

Reputation: 27

enum UserRole {
 ADMIN='ADMIN',
 USER=''USER,
 CLIENT='CLIENT'
 STUDENT='STUDENT'
}

userRoles = UserRole
this.userRoles.delete('ADMIN')
this.userRoles.delete('USER')

Upvotes: 0

user1234
user1234

Reputation: 8978

As of TypeScript 2.8 and the addition of conditional types, you can use the built-in Exclude to exclude certain enum values:

const enum Errcode {
    Ok=0,
    Error=1,
    AccessDeny=201,
    PostsNotFound=202,
    TagNotFound=203,
    //...
}

type SuccessErrcode = Errcode.Ok;
type NotFoundError = Errcode.PostsNotFound|Errcode.TagNotFound;
type ErrorErrcode = Exclude<Errcode, Errcode.Ok>;

Typescript Playground

Upvotes: 231

Russ
Russ

Reputation: 631

If you want the "typed" version of the above (plus some refinements):

enum Country {
  Afghanistan = 'Afghanistan',
  Albania = 'Albania',
  Guam = 'Guam',
  UnitedStates = 'United States'
}

const usAndIslands: Country[] = [Country.Guam, Country.UnitedStates];

const nonUsCountries: { [x: string]: string | Country } = Object.entries(Country)
  .filter((country: [string, string | number]): boolean =>
    typeof country[1] === 'number' && !usAndIslands.includes(country[1]))
  .reduce((dict: {}, [key, value]: [string, string | Country): { [x: string]: string | Country } =>
    Object.assign(dict, { [key]: value }), {});

console.log(nonUsCountries);

And if you want the result to be a Country[], you would use this:

const nonUsCountries: Country[] = Object.entries(Country)
  .filter((country: [string, string | number]): boolean =>
    typeof country[1] === 'number' && !usAndIslands.includes(country[1]))
  .map(([, value]: [string, string | Country): Country => val as Country);

And here is an abstracted function you can use with any numeric enum:

/**
 * Returns an array of enum values given an enum and disallowed values.
 *
 * @param myEnum The enum name.
 * @param disallowedValues An array of values of myEnum that should not be returned.
 *
 * @return An array of all myEnum values without the disallowedValues.
 */
export const numericEnumFilterOut: Function =
  <R extends number, T extends {[key: string]: R}>(myEnum: T, disallowedValues: R[]): R[] =>
    Object.entries(myEnum)
      .filter((type: [string, string | R]): boolean =>
        typeof type[1] === 'number' && !disallowedValues.includes(type[1]))
      .map((type: [string, R]): [string, R] => type as [string, R])
      .map(([, value]: [string, R]): R => value);

Usage:

const nonUsCountries: Country[] =
  numericEnumFilterOut(Country, [Country.Guam, Country.UnitedStates]);

console.log(nonUseCountries);

Upvotes: 0

cdonner
cdonner

Reputation: 37668

This took quite a bit of time to figure out. There really has not been an answer to this question in all its incarnations that worked for me. I needed to be able to treat the new value set the same way as the original enum, i.e. populate selection lists with it etc.

Then I was stuck for a bit on the fact that Object.entries() produces an array of arrays from a dictionary, and I had to convert this back into a dictionary so that my existing code continued to work with either type.

reduce() to the rescue!

Here is my solution (credit goes to Jake Holzinger's answer to this question):

enum Countries {
  UnitedStates = 'United States',
  Afghanistan = 'Afghanistan',
  Albania = 'Albania'}

const UsAndIslands = [
  Countries.UnitedStates];

const NonUsCountries = Object.entries(Countries)
  .filter((country) => !UsAndIslands.includes(country[1]))
  .reduce((dict, [key, value]) => Object.assign(dict, { [key]: value }), {});

console.log("Countries", Countries);
console.log("NonUsCountries", NonUsCountries);

Try it out.

Upvotes: 3

Stewart_R
Stewart_R

Reputation: 14485

You would first define the more granular types. Perhaps something like this:

enum ErrorCode {
    Error = 1,
    AccessDeny = 201,
    PostsNotFound = 202,
    TagNotFound = 203,
}

enum SuccessCode {
    Ok = 0
}

You can then define a Union Type to be either a SuccessCode or a ErrorCode:

type ResultCode = ErrorCode | SuccessCode;

which you can then use like this:

const myResult1: ResultCode = ErrorCode.AccessDeny;
const myResult2: ResultCode = SuccessCode.Ok;

Upvotes: 11

Related Questions