3

The case is you have a list of constants or Enum how you can do it ?

Here is the pseudo example code:

enum MyList
{
    A,
    B
}

enum MyList2
{
    C
}


function test<T>(input:MyList | T):void
{

}

test<MyList2>(123) // compiler does not identify that 123 is not a supported
4
  • The problem is actually much simpler than that. Commented Jul 11, 2019 at 15:03
  • @T.J.Crowder Hi, well imagine that you want to allow test(...) also to accept other possible values and if no other value is specified to accept MyList. Commented Jul 11, 2019 at 15:08
  • A longstanding issue with numeric enums. Not to mention that they also can act as bit fields, so essentially any number is assignable to any numeric enum, and vice versa. Commented Jul 11, 2019 at 15:10
  • @Hivaga - My point is that while it's a problem with a union of enums, that's just because...it's a problem with enums. Commented Jul 11, 2019 at 15:22

1 Answer 1

7

This is a longstanding issue with numeric enums. For whatever reason (some backwards compatibility they can't break), a number is seen as assignable to a numeric enum, so you can do this with no error:

enum E {
  V = 100
}
const num = 100;
const e: E = num; // no error 🤔

What's more, numeric enums are also intended to act as bit fields, and therefore they intentionally don't require that a value of an numeric enum type is one of the specific declared values:

enum Color {
  RED = 1,
  GREEN = 2,
  BLUE = 4
}
const red: Color = Color.RED; // 1
const yellow: Color = Color.RED | Color.GREEN; // 3 🤔
const white: Color = Color.RED | Color.GREEN | Color.BLUE; // 7 🤔
const octarine: Color = Math.pow(Color.BLUE - Color.RED, Color.GREEN); // 9 🤪

Yeah, I don't know why you can do any math you'd like with a numeric enum, but you can. The upshot is, essentially any number is assignable to any numeric enum, and vice versa.


If you want to prevent this you might want to give up on actual enums and instead make your own types and values with behavior you control. It's more verbose but it might meet your needs:

const MyList = {
  A: 0,
  B: 1
} as const;
type MyList = typeof MyList[keyof typeof MyList]

const MyList2 = {
  C: 0
} as const;
type MyList2 = typeof MyList2[keyof typeof MyList2]

Those behave similarly to your old enums (although there are some missing types) but they will be much more strict about behavior:

function test<T>(input: MyList | T): void {}

test(0); // okay
test(1); // okay
test(2); // okay, 2 is inferred as T
test<MyList2>(123); // error! 123 is not assignable to 0 | 1

Link to code

Sign up to request clarification or add additional context in comments.

1 Comment

Incredible :) how you get to know all those details.Proposed solution is nice.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.