Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using enums as types allows values that don't exist in the enum

I have this typescript code (typescript playground):

const enum Something {
  None = 0,
  Email = 10,
  All = 20
}

const enum Other{
  Email = 10;
  Value = 15;
}

interface Foo {
  prop: Something
}
const value2: Something = Something.None;

// Why can 15 be assigned if it's not in the enum?
const value: Something = 15;

// This errors:
const otherValue: Something = 'asdf';

const value3: Something = Something.NotExists;

const value4: Something = Other.Value;

const value5: Something = Other.Email;

I don't understand why 15 is an acceptable value in this caase. 15 is not a value of the enum, so shouldn't it throw?

like image 969
Narretz Avatar asked Mar 09 '18 09:03

Narretz


People also ask

Are enums value types?

Enum is Reference Type or Value Type? enumerations are of value type. They are created and stored on the stack and passed to the methods by making a copy of the value (by value). Enums are value type.

Which data type can be used in enum?

The enum can be of any numeric data type such as byte, sbyte, short, ushort, int, uint, long, or ulong. However, an enum cannot be a string type.

Can I use enum as a type in TypeScript?

Enums are one of the few features TypeScript has which is not a type-level extension of JavaScript. Enums allow a developer to define a set of named constants. Using enums can make it easier to document intent, or create a set of distinct cases. TypeScript provides both numeric and string-based enums.


1 Answers

(update 2021-06-10, this is unchanged by the TS4.3 update for union enums)

This is (maybe surprisingly) intended behavior. Numeric enums in TypeScript are sometimes used for bitwise operations, where the listed values are treated as flags. And, as stated by @RyanCavanaugh in a comment on a reported issue about this:

We don't distinguish between flag and non-flag enums, so a number that is above [or not equal to] any given enum member isn't necessarily invalid. For example

enum Flags { Neat = 1, Cool = 2, Great = 4 } // like saying Neat | Cool | Great var x: Flags = 7;

So even though 7 is not equal to any one of the listed Flags enum values, you can still get it by performing bitwise operations of the listed values. I'm pretty sure that the compiler doesn't do any restriction other than checking that the value is a number.

In your case, even though none of the Something enumerated values are 15, it doesn't stop you from doing the (useless and dubiously sane) following:

const value: Something = (Something.Email | Something.All) >> 1;

which amounts to the same thing (10 | 20 evaluates to 30, and 30 >> 1 is 15).


Note that this bitwise stuff doesn't apply to string-based enums, so one way to deal with this is to change your numbers to string literals:

const enum Something {
  None = '0',
  Email = '10',
  All = '20'
}
const value: Something = '15'; // error, as desired

and the compiler warns you that '15' is not a valid value for Something.

Hope that helps. Good luck!

like image 100
jcalz Avatar answered Oct 22 '22 12:10

jcalz