Description
Search Terms
number assignable enum
Suggestion
Remove assignability of number to enum types.
Type number is currently assignable to numeric enum types. I am aware that this was intentionally implemented as a special case for convenience.
With recent improvements to generics (particularly conditional types), I believe that the convenience of number -> enum assignability may now be outweighed by the power of "distinctness" of NOT allowing assignability in that direction.
Removing this special case behavior would also probably have the benefit of simplifying some code in the TypeScript compiler and reducing risk of future bugs. Here's an example of a bug that was caused by the special case behavior of number->enum assignability: #10738
Use Cases
If number is NOT assignable to enum types, then it would enable the use of conditional types to distinguish numeric literal/enum types from plain old number, similar to the difference between
string literal/enum types and plain string.
My particular use case is probably too complex to try to present a full example, but here's a high level description...
I am generating typescript code/interfaces from a Swagger document for making API calls. There are two parts to this:
- The compile-time static types of interfaces for the API's "models" (data structures). This is obviously used for static compile-time analysis to ensure that API request/response data is used properly. This includes generated enum types (both number and string) for properties that are defined as enums in the Swagger schema.
- The run-time representation of the schema for the API's "models". This is used to generate forms, grid column configurations, etc., at run time based on the API "model" schemas. At the core of this is a discriminated union of interfaces containing the pertinent schema info for all supported data types for the properties of the API's "models". The interface for each data type is different. For example, "Number" data can have min/max values, indication of whether it's integer or floating point. "NumberEnum" simply contains a list of valid numeric values and generated names for each value (which match the actual names of values in corresponding generated enum type from [1] above). The Swagger JSON document is parsed at run time and converted into appropriate data structures to represent the API's "models" in terms of these data type schema interfaces.
In order to link these two halves together and have strictly typed run-time schema utilities that make use of the compile time types of the "model" interfaces, part of my solution requires a helper type that maps from the type of a property in a "model" interface to the type of that field's schema definition interface.
Currently, I can accurately distinguish between string enum vs plain string types, but I cannot do the same for number enum vs plain number. Numeric enum properties incorrectly have their types mapped to the data schema interface type for plain numeric data.
Examples
Here's an over-simplified example of the type mapping I am attempting, with comments indicating the results I would expect if number is NOT assignable to number enum:
enum StringEnum {
A = "A",
B = "B"
}
enum NumberEnum {
A,
B
}
type Test<T> = T extends number
? (number extends T ? "number" : "number-enum")
: T extends string
? (string extends T ? "string" : "string-enum")
: never;
let testString: Test<string>; // type: "string"
let testStringEnum: Test<StringEnum>; // type: "string-enum"
let testNumber: Test<number>; // type: "number"
let testNumberEnum: Test<NumberEnum>; // type: "number-enum"
With the current implementation of number->enum assignability, the type of testNumberEnum
is "number"
instead of "number-enum"
.
Aside from that, there's all the usual benefits of the stricter typing/assignability: plain numbers will need to be explicitly cast to an enum type before assigning (forces developer to consider whether it is correct).
Then there's the breaking change and inconvenience: plain numbers will need to be explicitly cast to an enum type before assigning (forces developer to consider whether it is correct).
Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript / JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. new expression-level syntax)