TypeScript Version: 2.7.1
Search Terms: enum numeric number type
Code
enum TestStringEnum {
a = "a",
b = "b",
}
function testStringEnum(test: TestStringEnum) {
return test;
}
testStringEnum(9); // fails (expected)
testStringEnum("9"); // fails (expected)
testStringEnum(TestStringEnum.a); // passes (expected)
testStringEnum("a"); // fails (???)
type TestNumberUnion = 4 | 5;
function testNumberUnion(test: TestNumberUnion) {
return test;
}
testNumberUnion(9); // fails (expected)
testNumberUnion("9"); // fails (expected)
testNumberUnion(4); // passes (expected)
enum TestNumberEnum {
a = 1,
b = 2,
}
function testNumberEnum(test: TestNumberEnum) {
return test;
}
testNumberEnum(9); // passes (not expected)
testNumberEnum("9"); // fails (expected)
testNumberEnum(TestNumberEnum.a); // passes (expected)
testNumberEnum(1); // passes (???)
Expected behavior:
The behavior of a string backed enum should match a number backed enum.
Actual behavior:
A number backed enum is treated as equivalent to any number.
Playground Link: playground link
Related Issues:
This is an unfortunate behavior we have due to scenarios where people use enums for things like bit-flags, or other situations where numeric operations on enums should "implicitly" yield other enum members.
Please see https://github.com/Microsoft/TypeScript/issues/18409 for more details.
I'd expect if someone's using enums for bit flags or are doing numeric operations on them, they'd need to be explicit, in order to prevent holes like the one this issue describes.
For the following (which is currently legal), I'd expect errors when calling isMasked(123, 123) and when doing val & mask.
enum BitMask {
a = 2,
b = 4,
}
function isMasked(val: number, mask: BitMask) {
return val & mask;
}
isMasked(123, 123);
isMasked(123, BitMask.a);
I'd expect this to be the way this works:
enum BitMask {
a = 2,
b = 4,
}
function isMasked(val: number, mask: BitMask) {
return val & (mask as number); // or allow this to work since the compiler knows BitMask is numeric
}
isMasked(123, 123 as BitMask); // 123 isn't a member of BitMask
isMasked(123, BitMask.a);
Currently, there's no way to ensure a parameter is an enum instead of a number if that enum is numeric. From someone who's reading the code's perspective, the type of enum (numeric or string) isn't known.
In #18409, there's inconstancy between they way string enums and numeric enums work.
The example given uses strings:
enum Vals {
ONE = "one",
TWO = "two",
}
enum AltVals {
UNO = "one",
}
declare let Vals_ONE: Vals.ONE;
declare let AltVals_Uno: AltVals.UNO;
declare let One: "one";
One = Vals_ONE; // OK
One = AltVals_Uno; // OK
Vals_ONE = One; // Error
Vals_ONE = AltVals_Uno; // Error
If we switch to numbers, it behaves differently:
enum ValsNumeric {
ONE = 1,
TWO = 2,
}
enum AltValsNumeric {
UNO = 1,
}
declare let ValsNumeric_ONE: ValsNumeric.ONE;
declare let AltValsNumeric_Uno: AltValsNumeric.UNO;
declare let OneNumeric: 1;
OneNumeric = ValsNumeric_ONE; // OK
OneNumeric = AltValsNumeric_Uno; // OK
ValsNumeric_ONE = OneNumeric; // OK (unexpected) <---------------
ValsNumeric_ONE = AltValsNumeric_Uno; // Error
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.