TypeScript enum conversion / casting
Asked Answered
C

5

8

This statement fails. How could I cast from one enum into another (which are identical)

enum Enum1 {
  Key1 = 'key'
}

enum Enum2 {
  Key1 = 'key'
}

const key = Enum1.Key1
const key2 = key as Enum2
Catechist answered 8/3, 2018 at 8:14 Comment(2)
Can you write down a practical use case where you need to do this? It'll help us understand the problem better.Irreclaimable
I sometimes have the same enum defined in multiple places and unable to import from one to another due to dependency boundaries. So i thought keeping them exactly the same would make TS compiler happy, but apparently not.Catechist
Z
5

Here is a solution that works for number values. However, note this is "dangerous" as there is no confirmation/validation about the conversion occurring, so probably doesn't satisfy your desire for compile-time checking. The bottom line is you have to cast to some intermediate compatible type (number, string, any, unknown) before casting to 2nd enum, and by so doing, you have detached any meaningful semantic checking. Anytime you cast you are giving up compile time checking by definition.

enum SeverityLevel {
    Verbose = 0,
    Warning = 1
}

enum InternalSeverity {
    Verbose = 0,
    Warning = 1
}


function CallMe(severity: SeverityLevel) {
    console.log(`SeverityLevel: ${severity}`);
}

function Convert(severity: InternalSeverity) {
    console.log(severity);
    console.log(SeverityLevel[severity]);
    console.log(InternalSeverity[severity]);

    CallMe(severity as number as SeverityLevel);
}

Convert(InternalSeverity.Warning);

Output

It might be better to write a verbose conversion function that maps values explicitly and can check that, for example, warning is the same in both enums, like so:

switch (severity) {
   case SeverityLevel.Warning:
      return InternalSeverity.Warning;
      break;

This allows conversion between enums, is resilient to changes in the underlying values (assuming the purpose of the enum is to use names to represent values and that the values themselves are irrelevant) and satisfies compile time checks (insofar as if someone removes the key from the enum it will break). If the values are more important than the names, then you may need a slightly different approach.

Zone answered 8/6, 2022 at 21:56 Comment(0)
T
1

In case you use number instead of string as the enum values this would work:

enum Enum1 {
  Key1 = 2
}

enum Enum2 {
  Key1 = 2
}

const key = Enum1.Key1
const key2 = Enum2[Enum1[key]];
Taipan answered 19/7, 2018 at 12:2 Comment(1)
Actually, this also works for string values if enum keys and values are the same: typescriptlang.org/play?#code/…Orlena
C
1

You can cast to string first and then to the desired enum:

const key2 = key as string as Enum2
Cumbrous answered 25/3, 2020 at 15:7 Comment(0)
K
0

It seems indeed that Typescript doesn't bother checking the possible values, so it doesn't notice these enums are compatible. What I am doing right now is

const key2 = key as Enum1 & Enum2

It is not perfect, since it does not enforce the enum's compatibility. However still better than widening to string or any.

Keyes answered 6/3, 2021 at 16:12 Comment(0)
L
-1

At runtime the variable will contain the enum value (key in your case) So you can just cast through any and it should work

const key = Enum1.Key1
const key2: Enum2 = key as any
Luminous answered 8/3, 2018 at 8:22 Comment(3)
I understand this works at runtime. I was hoping to find a compile time safe way of doing this. (e.g. if Enum2 def changed, it should complain)Catechist
@Catechist don't think there is a way to do this with enums.... maybe if you don't use enums .. I fiddled with some types that simulate enums, but allow assignment between enums if the same value exists, using string literalsLuminous
Yea seems that's the workaround - string union rather than enum.Catechist

© 2022 - 2024 — McMap. All rights reserved.