Typescript enum encapsulating a boolean value per enum instance

6.2k views Asked by At

In Typescript, what is the idiomatic way to have a boolean value assigned for each enum instance?

Say I have an enum for various error codes. For each of the error codes, I have a boolean stating if the error must be exposed to the end user or not. In Java, I would do like,

enum MyError {
    ERROR1(true),
    ERROR2(false),
    ERROR3(false);

    private boolean expose;

    public boolean shouldExpose() {
        return expose;
    }

    MyError(boolean expose) {
        this.expose = expose;
    }
}

Here, the boolean info (whether the error must be exposed to user or not) is encapsulated within the enum itself.

 MyError myError = MyError.ERROR1;
 System.out.println(myError.shouldExpose()); //true

How can I do this in Typescript as TS doesn't allow boolean values in enums? Should I use a class or a type here?

The goal is to have the boolean information contained in the enum/class/type. Hence, I don't want to create a wrapper type like

{
    error: MyError
    expose: boolean
}

as it can lead to wrong configuration/mappings (it becomes possible to have MyError.ERROR1 with expose as false or vice-versa for other errors).

5

There are 5 answers

3
Lazar Ljubenović On BEST ANSWER

You wouldn't try to fit this information about the errors in the enumeration itself. The enumeration is simply for listing (well, enumerating) the errors. Their exposeness should be saved in a mapping between the error and the answer to the "is it exposed?" question.

// values don't matter; you can also skip them if you don't care for them
enum Error {
  Fatal = 1,
  Disk = 2,
  NoInternet = 3,
}

const IS_EXPOSED: Readonly<Record<Error, boolean>> = {
  [Error.Fatal]: false,
  [Error.Disk]: true,
  [Error.NoInternet]: true,
}

TypeScript's type system will throw an error if you miss an enumeration in the IS_EXPOSED object, so it's completely type-safe.

6
Murat Karagöz On

If you do not care about the enumerical order then you could leverage numeric enums e.g.

enum MyError  {
  ERROR1 = 1,
  ERROR2 = 0,
  ERROR3 = 0,
}

console.log(MyError.ERROR1); // 1 == true
console.log(MyError.ERROR2); // 0 == false
console.log(MyError.ERROR3); // 0 == false

console.log(Boolean(MyError.ERROR1)); // true
console.log(Boolean(MyError.ERROR2)); // false
console.log(Boolean(MyError.ERROR3)); // false

EDIT:

You could use namespace merging e.g.

namespace MyError {
    export function shouldExpose(error:MyError) {
        return Boolean(error);
    }
}

console.log(MyError.shouldExpose(MyError.ERROR1)); // true
0
Lesiak On

You might want to give up on actual enums and instead make your own set of constants. It's more verbose but it might meet your needs.

const MyError = {
  error1: {
    name: 'ERROR1',
    expose: true
  },
  error2: {
    name: 'ERROR2',
    expose: false
  },
  error3: {
    name: 'ERROR3',
    expose: false
  }
} as const;

type MyError = typeof MyError[keyof typeof MyError]


function x(err: MyError) {
  console.log(err);
}

x(MyError.error1);
x({name: 'ERROR1', expose: true});
x({name: 'ERROR1', expose: false}); // EXPECTED ERROR

function getError(): MyError {
  return MyError.error1;
}

var e: MyError = getError();
console.log(MyError.error1 == e);  // true
console.log(MyError.error2 == e);  // false

Playground link

0
jsejcksn On

You can create a class which extends the native Error class:

TS Playground

enum ErrorVariant {
  ERROR1 = 1,
  ERROR2 = 2,
  ERROR3 = 3,
}

class MyError<T extends boolean> extends Error {
  constructor (
    readonly variant: ErrorVariant,
    private readonly expose: T,
    message?: string,
  ) {
    super(message);
  }

  shouldExpose (): T {
    return this.expose;
  }
}

const myError1 = new MyError(ErrorVariant.ERROR1, false, 'Uh oh');
myError1.shouldExpose() // false
myError1.variant // ErrorVariant => 1

const myError2 = new MyError(ErrorVariant.ERROR2, true, 'Uh oh');
myError2.shouldExpose() // true
myError2.variant // ErrorVariant => 2
myError2.message // string
0
captain-yossarian from Ukraine On

It might be a good idea to use bitmask for this purpose:

enum CustomErrors {
    Error1 = 1 << 0,
    Error2 = 1 << 1,
    Error3 = 1 << 2
}

const error_1 = 0b001
const error_1_AND_2 = 0b011
const error_1_AND_3 = 0b101
const error_3 = 0b101

const isError1 = CustomErrors.Error1 & error_1 // 1, true
const isError2 = CustomErrors.Error2 & error_1 // 0, false
const isError1_2 = CustomErrors.Error2 & error_1_AND_2 // 2, true
const isError1_2_ = CustomErrors.Error3 & error_3 // 4, true

Playground