TypeScript
TypeScript copied to clipboard
Enum unification and improvements
Since #9407 TypeScript has had two kinds of enum types: Numeric enum types and literal enum types (also known as union enum types). Numeric enum types have permitted enum members to have computed values, but not literal types; whereas literal enum types have required members to be declared using simple numeric or string literals only. The distinction between these two kinds of enums is subtle and has been the source of a fair amount of confusion over time.
With this PR we unify the two kinds into a single hybrid that incorporates the best of both worlds. Specifically, enums are always represented as unions of their member types, member values may be computed by constant or non-constant expressions, and members with constant values are given literal types. The PR only affects type checking of enums. There are no changes to the emitted code (except for better inlining of constant expressions in some cases).
An enum declaration now declares a type representing the entire enum and types representing each member (similar to literal enum types). For example,
enum E {
A = 10 * 10, // Numeric literal enum member
B = "foo", // String literal enum member
C = bar(42) // Opaque computed enum member
}
declares a type E
and types E.A
, E.B
, and E.C
, where E
is the union E.A | E.B | E.C
.
An enum member has a literal enum type when it is initialized by a constant expression or when it has no initializer and is given an auto-incremented numeric value. In the example above, E.A
and E.B
denote enum literal types with values 100
and "foo"
respectively.
An enum member has a unique and opaque enum member type when it is initialized by a non-constant expression. Non-constant initializer expressions must be assignable to type number
and are not permitted in const
enum declarations. In the example above, E.C
denotes a unique enum member type representing the value computed by the non-constant initializer expression. This unique type is assignable to type number
, but otherwise incompatible with other types.
An expression is considered a constant expression if it is
- a number or string literal,
- a unary
+
,-
, or~
applied to a numeric constant expression, - a binary
+
,-
,*
,/
,%
,**
,<<
,>>
,>>>
,|
,&
,^
applied to two numeric constant expressions, - a binary
+
applied to two constant expressions whereof at least one is a string, - a template expression where each substitution expression is a constant expression,
- a parenthesized constant expression,
- a dotted name (e.g.
x.y.z
) that references aconst
variable with a constant expression initializer and no type annotation, - a dotted name that references an enum member with an enum literal type, or
- a dotted name indexed by a string literal (e.g.
x.y["z"]
) that references an enum member with an enum literal type.
Note that constant expressions are not permitted to make forward references--any value referenced by a constant expression must have been previously declared.
Some examples:
const BaseValue = 10;
const Prefix = "/data";
const enum Values {
First = BaseValue, // 10
Second, // 11
Third // 12
}
const enum Routes {
Parts = `${prefix}/parts`, // "/data/parts"
Invoices = `${prefix}/invoices` // "/data/invoices"
}
NOTE: This PR is technically a breaking change because checking of the new unified enum types is stricter than the old numeric enum types.
Fixes #27976. Fixes #35875. Fixes #40793. Fixes #43902.
@typescript-bot pack this
Heya @RyanCavanaugh, I've started to run the tarball bundle task on this PR at 50a952b95412af80fa2d860aba8df07eb06d0c6a. You can monitor the build here.
@typescript-bot test this @typescript-bot user test this inline @typescript-bot run dt @typescript-bot perf test faster
Heya @ahejlsberg, I've started to run the extended test suite on this PR at 50a952b95412af80fa2d860aba8df07eb06d0c6a. You can monitor the build here.
Heya @ahejlsberg, I've started to run the abridged perf test suite on this PR at 50a952b95412af80fa2d860aba8df07eb06d0c6a. You can monitor the build here.
Update: The results are in!
Heya @ahejlsberg, I've started to run the parallelized Definitely Typed test suite on this PR at 50a952b95412af80fa2d860aba8df07eb06d0c6a. You can monitor the build here.
Heya @ahejlsberg, I've started to run the diff-based user code test suite on this PR at 50a952b95412af80fa2d860aba8df07eb06d0c6a. You can monitor the build here.
Update: The results are in!
@ahejlsberg The results of the perf run you requested are in!
Here they are:
Comparison Report - main..50528
Metric | main | 50528 | Delta | Best | Worst |
---|---|---|---|---|---|
Angular - node (v14.15.1, x64) | |||||
Memory used | 338,727k (± 0.00%) | 338,717k (± 0.01%) | -9k (- 0.00%) | 338,668k | 338,757k |
Parse Time | 2.08s (± 0.70%) | 2.09s (± 0.93%) | +0.02s (+ 0.82%) | 2.06s | 2.15s |
Bind Time | 0.80s (± 0.62%) | 0.80s (± 0.75%) | +0.00s (+ 0.00%) | 0.79s | 0.81s |
Check Time | 5.82s (± 0.58%) | 5.83s (± 0.27%) | +0.01s (+ 0.14%) | 5.79s | 5.87s |
Emit Time | 6.18s (± 0.49%) | 6.22s (± 0.72%) | +0.04s (+ 0.58%) | 6.16s | 6.37s |
Total Time | 14.87s (± 0.27%) | 14.93s (± 0.40%) | +0.06s (+ 0.41%) | 14.85s | 15.11s |
Compiler-Unions - node (v14.15.1, x64) | |||||
Memory used | 192,654k (± 0.02%) | 192,983k (± 0.37%) | +329k (+ 0.17%) | 192,631k | 195,896k |
Parse Time | 0.86s (± 0.52%) | 0.85s (± 0.87%) | -0.01s (- 0.58%) | 0.84s | 0.87s |
Bind Time | 0.49s (± 0.75%) | 0.49s (± 0.82%) | +0.00s (+ 0.62%) | 0.48s | 0.50s |
Check Time | 6.70s (± 0.55%) | 6.73s (± 0.69%) | +0.03s (+ 0.43%) | 6.64s | 6.85s |
Emit Time | 2.41s (± 1.02%) | 2.40s (± 0.72%) | -0.01s (- 0.54%) | 2.35s | 2.43s |
Total Time | 10.46s (± 0.45%) | 10.47s (± 0.51%) | +0.02s (+ 0.16%) | 10.35s | 10.62s |
Monaco - node (v14.15.1, x64) | |||||
Memory used | 326,505k (± 0.01%) | 326,536k (± 0.00%) | +31k (+ 0.01%) | 326,510k | 326,581k |
Parse Time | 1.57s (± 0.59%) | 1.60s (± 1.50%) | +0.03s (+ 1.65%) | 1.57s | 1.69s |
Bind Time | 0.73s (± 0.85%) | 0.73s (± 0.68%) | -0.00s (- 0.41%) | 0.72s | 0.74s |
Check Time | 5.73s (± 0.51%) | 5.78s (± 0.51%) | +0.05s (+ 0.86%) | 5.73s | 5.85s |
Emit Time | 3.35s (± 0.61%) | 3.35s (± 0.67%) | -0.00s (- 0.12%) | 3.30s | 3.40s |
Total Time | 11.39s (± 0.42%) | 11.45s (± 0.44%) | +0.06s (+ 0.55%) | 11.35s | 11.55s |
TFS - node (v14.15.1, x64) | |||||
Memory used | 289,645k (± 0.00%) | 289,648k (± 0.01%) | +3k (+ 0.00%) | 289,614k | 289,687k |
Parse Time | 1.31s (± 0.83%) | 1.31s (± 0.80%) | +0.01s (+ 0.38%) | 1.30s | 1.34s |
Bind Time | 0.78s (± 2.29%) | 0.80s (± 0.75%) | +0.01s (+ 1.40%) | 0.78s | 0.81s |
Check Time | 5.36s (± 0.54%) | 5.35s (± 0.62%) | -0.01s (- 0.24%) | 5.27s | 5.44s |
Emit Time | 3.60s (± 0.77%) | 3.57s (± 0.76%) | -0.03s (- 0.75%) | 3.51s | 3.63s |
Total Time | 11.05s (± 0.38%) | 11.03s (± 0.59%) | -0.03s (- 0.25%) | 10.87s | 11.20s |
material-ui - node (v14.15.1, x64) | |||||
Memory used | 438,087k (± 0.06%) | 438,212k (± 0.00%) | +125k (+ 0.03%) | 438,180k | 438,251k |
Parse Time | 1.86s (± 0.59%) | 1.87s (± 0.59%) | +0.01s (+ 0.38%) | 1.84s | 1.89s |
Bind Time | 0.59s (± 0.62%) | 0.59s (± 1.10%) | +0.00s (+ 0.34%) | 0.58s | 0.61s |
Check Time | 13.06s (± 0.95%) | 12.92s (± 0.81%) | -0.14s (- 1.06%) | 12.72s | 13.15s |
Emit Time | 0.00s (± 0.00%) | 0.00s (± 0.00%) | 0.00s ( NaN%) | 0.00s | 0.00s |
Total Time | 15.51s (± 0.81%) | 15.38s (± 0.72%) | -0.13s (- 0.83%) | 15.16s | 15.62s |
xstate - node (v14.15.1, x64) | |||||
Memory used | 547,388k (± 0.00%) | 547,403k (± 0.00%) | +15k (+ 0.00%) | 547,362k | 547,442k |
Parse Time | 2.60s (± 0.45%) | 2.62s (± 0.47%) | +0.02s (+ 0.73%) | 2.60s | 2.65s |
Bind Time | 0.97s (± 0.70%) | 0.98s (± 0.68%) | +0.01s (+ 0.62%) | 0.97s | 0.99s |
Check Time | 1.54s (± 0.62%) | 1.54s (± 0.42%) | -0.00s (- 0.13%) | 1.53s | 1.55s |
Emit Time | 0.07s (± 4.13%) | 0.07s (± 3.14%) | -0.00s (- 1.39%) | 0.07s | 0.08s |
Total Time | 5.20s (± 0.42%) | 5.22s (± 0.21%) | +0.02s (+ 0.38%) | 5.19s | 5.24s |
Machine Name | ts-ci-ubuntu |
---|---|
Platform | linux 4.4.0-210-generic |
Architecture | x64 |
Available Memory | 16 GB |
Available Memory | 15 GB |
CPUs | 4 × Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz |
- node (v14.15.1, x64)
- Angular - node (v14.15.1, x64)
- Compiler-Unions - node (v14.15.1, x64)
- Monaco - node (v14.15.1, x64)
- TFS - node (v14.15.1, x64)
- material-ui - node (v14.15.1, x64)
- xstate - node (v14.15.1, x64)
Benchmark | Name | Iterations |
---|---|---|
Current | 50528 | 10 |
Baseline | main | 10 |
Developer Information:
@ahejlsberg Here are the results of running the user test suite comparing main
and refs/pull/50528/merge
:
Everything looks good!
Heya @ahejlsberg, I've run the RWC suite on this PR - assuming you're on the TS core team, you can view the resulting diff here.
Hey @RyanCavanaugh, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json
like so:
{
"devDependencies": {
"typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/133037/artifacts?artifactName=tgz&fileId=3F948668C1BD8758CB1C8E59919B3D6E945DCF3815094A729B98FCE15A366B7E02&fileName=/typescript-4.9.0-insiders.20220829.tgz"
}
}
and then running npm install
.
There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/[email protected]"
.;
@typescript-bot test this @typescript-bot user test this inline @typescript-bot run dt @typescript-bot perf test faster @typescript-bot test top100
Heya @ahejlsberg, I've started to run the diff-based top-repos suite on this PR at 3f66f6c499f9170e5d114e977a1512c0fbd12024. You can monitor the build here.
Update: The results are in!
Heya @ahejlsberg, I've started to run the diff-based user code test suite on this PR at 3f66f6c499f9170e5d114e977a1512c0fbd12024. You can monitor the build here.
Update: The results are in!
Heya @ahejlsberg, I've started to run the abridged perf test suite on this PR at 3f66f6c499f9170e5d114e977a1512c0fbd12024. You can monitor the build here.
Update: The results are in!
Heya @ahejlsberg, I've started to run the extended test suite on this PR at 3f66f6c499f9170e5d114e977a1512c0fbd12024. You can monitor the build here.
Heya @ahejlsberg, I've started to run the parallelized Definitely Typed test suite on this PR at 3f66f6c499f9170e5d114e977a1512c0fbd12024. You can monitor the build here.
@ahejlsberg Here are the results of running the user test suite comparing main
and refs/pull/50528/merge
:
Everything looks good!
@ahejlsberg The results of the perf run you requested are in!
Here they are:
Comparison Report - main..50528
Metric | main | 50528 | Delta | Best | Worst |
---|---|---|---|---|---|
Angular - node (v16.17.1, x64) | |||||
Memory used | 352,477k (± 0.02%) | 352,459k (± 0.01%) | -18k (- 0.01%) | 352,332k | 352,517k |
Parse Time | 1.90s (± 0.86%) | 1.90s (± 0.86%) | +0.01s (+ 0.42%) | 1.87s | 1.93s |
Bind Time | 0.75s (± 0.50%) | 0.75s (± 0.77%) | +0.00s (+ 0.67%) | 0.74s | 0.77s |
Check Time | 5.69s (± 0.34%) | 5.70s (± 0.37%) | +0.01s (+ 0.12%) | 5.64s | 5.75s |
Emit Time | 6.09s (± 0.60%) | 6.13s (± 0.53%) | +0.04s (+ 0.57%) | 6.09s | 6.22s |
Total Time | 14.43s (± 0.45%) | 14.47s (± 0.35%) | +0.05s (+ 0.34%) | 14.39s | 14.65s |
Compiler-Unions - node (v16.17.1, x64) | |||||
Memory used | 197,729k (± 0.37%) | 197,791k (± 0.02%) | +62k (+ 0.03%) | 197,684k | 197,878k |
Parse Time | 0.78s (± 0.60%) | 0.78s (± 0.38%) | +0.00s (+ 0.26%) | 0.78s | 0.79s |
Bind Time | 0.45s (± 0.75%) | 0.45s (± 0.80%) | +0.00s (+ 0.22%) | 0.45s | 0.46s |
Check Time | 6.42s (± 0.47%) | 6.47s (± 0.53%) | +0.05s (+ 0.72%) | 6.40s | 6.58s |
Emit Time | 2.25s (± 0.66%) | 2.27s (± 0.87%) | +0.03s (+ 1.25%) | 2.23s | 2.33s |
Total Time | 9.90s (± 0.43%) | 9.98s (± 0.43%) | +0.08s (+ 0.79%) | 9.91s | 10.09s |
Monaco - node (v16.17.1, x64) | |||||
Memory used | 331,132k (± 0.01%) | 331,279k (± 0.02%) | +148k (+ 0.04%) | 331,188k | 331,416k |
Parse Time | 1.44s (± 0.89%) | 1.43s (± 0.87%) | -0.00s (- 0.28%) | 1.41s | 1.47s |
Bind Time | 0.69s (± 0.75%) | 0.69s (± 0.72%) | +0.00s (+ 0.58%) | 0.69s | 0.71s |
Check Time | 5.46s (± 0.48%) | 5.48s (± 0.49%) | +0.02s (+ 0.44%) | 5.43s | 5.54s |
Emit Time | 3.26s (± 0.66%) | 3.24s (± 0.53%) | -0.01s (- 0.40%) | 3.22s | 3.30s |
Total Time | 10.85s (± 0.42%) | 10.86s (± 0.38%) | +0.01s (+ 0.12%) | 10.79s | 10.99s |
TFS - node (v16.17.1, x64) | |||||
Memory used | 294,064k (± 0.02%) | 294,065k (± 0.02%) | +2k (+ 0.00%) | 293,861k | 294,147k |
Parse Time | 1.23s (± 0.93%) | 1.22s (± 1.23%) | -0.01s (- 1.06%) | 1.19s | 1.26s |
Bind Time | 0.64s (± 0.62%) | 0.64s (± 0.90%) | -0.00s (- 0.16%) | 0.63s | 0.65s |
Check Time | 5.13s (± 0.57%) | 5.11s (± 0.42%) | -0.02s (- 0.35%) | 5.05s | 5.14s |
Emit Time | 3.49s (± 0.71%) | 3.49s (± 0.84%) | +0.00s (+ 0.11%) | 3.44s | 3.56s |
Total Time | 10.49s (± 0.57%) | 10.46s (± 0.47%) | -0.03s (- 0.26%) | 10.33s | 10.55s |
material-ui - node (v16.17.1, x64) | |||||
Memory used | 438,358k (± 0.00%) | 438,376k (± 0.01%) | +17k (+ 0.00%) | 438,313k | 438,530k |
Parse Time | 1.73s (± 1.15%) | 1.71s (± 0.97%) | -0.02s (- 1.15%) | 1.67s | 1.76s |
Bind Time | 0.54s (± 0.91%) | 0.54s (± 0.83%) | -0.01s (- 1.10%) | 0.53s | 0.55s |
Check Time | 12.49s (± 0.63%) | 12.52s (± 0.89%) | +0.02s (+ 0.19%) | 12.36s | 12.89s |
Emit Time | 0.00s (± 0.00%) | 0.00s (± 0.00%) | 0.00s ( NaN%) | 0.00s | 0.00s |
Total Time | 14.77s (± 0.60%) | 14.77s (± 0.76%) | +0.00s (+ 0.01%) | 14.60s | 15.12s |
xstate - node (v16.17.1, x64) | |||||
Memory used | 554,393k (± 0.01%) | 554,353k (± 0.01%) | -40k (- 0.01%) | 554,229k | 554,493k |
Parse Time | 2.31s (± 0.68%) | 2.31s (± 0.51%) | -0.00s (- 0.04%) | 2.28s | 2.33s |
Bind Time | 0.88s (± 0.39%) | 0.90s (± 2.29%) | +0.02s (+ 2.51%) | 0.87s | 0.95s |
Check Time | 1.43s (± 0.87%) | 1.42s (± 0.90%) | -0.01s (- 0.56%) | 1.39s | 1.44s |
Emit Time | 0.07s (± 0.00%) | 0.07s (± 0.00%) | 0.00s ( 0.00%) | 0.07s | 0.07s |
Total Time | 4.69s (± 0.44%) | 4.70s (± 0.38%) | +0.01s (+ 0.28%) | 4.67s | 4.75s |
Machine Name | ts-ci-ubuntu |
---|---|
Platform | linux 5.4.0-126-generic |
Architecture | x64 |
Available Memory | 16 GB |
Available Memory | 15 GB |
CPUs | 4 × Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz |
- node (v16.17.1, x64)
- Angular - node (v16.17.1, x64)
- Compiler-Unions - node (v16.17.1, x64)
- Monaco - node (v16.17.1, x64)
- TFS - node (v16.17.1, x64)
- material-ui - node (v16.17.1, x64)
- xstate - node (v16.17.1, x64)
Benchmark | Name | Iterations |
---|---|---|
Current | 50528 | 10 |
Baseline | main | 10 |
Developer Information:
Heya @ahejlsberg, I've run the RWC suite on this PR - assuming you're on the TS core team, you can view the resulting diff here.
@ahejlsberg Here are the results of running the top-repos suite comparing main
and refs/pull/50528/merge
:
Something interesting changed - please have a look.
Details
microsoft/vscode
4 of 53 projects failed to build with the old tsc and were ignored
src/tsconfig.tsec.json
-
error TS2739: Type '{ 4: string; 2: string; 8: string; }' is missing the following properties from type '{ 2: string; 4: string; 8: string; 16: string; 32: string; }': 16, 32
This change has surprisingly few effects: Performance, user code test suite, and DT test suite are unaffected. One new error in top100 test suite, correctly identifying an issue in vscode
. Small change in extended test suite, emit now uses inlined constants due to better constant expression folding.
There were four new errors in the compiler itself. See fixes here and here. I think these new errors are perfectly fine, even if one of them is a manifestation of #9998.
what happened to the back referencing Enum[Enum.Value] === "some value"
? and is it possible to allow symbols?
@typescript-bot pack this
Heya @jakebailey, I've started to run the tarball bundle task on this PR at f2f2d05deca0cdf5165c6ba6f6769483054dfb99. You can monitor the build here.
Hey @jakebailey, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json
like so:
{
"devDependencies": {
"typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/136892/artifacts?artifactName=tgz&fileId=24DEDAB836809F769071A0FB19D0B901663058A9D29C4F7F97BE396AF120751202&fileName=/typescript-4.9.0-insiders.20221021.tgz"
}
}
and then running npm install
.
There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/[email protected]"
.;
Would this have a problem?
const boat = 2;
const goat = 1;
enum NonTier6ItemGraphCodes {
Moose = boat,
Goose = goat,
Loose, // This would ALSO be 2 and stamp over Moose
}
Currently this throws an Enum member must have initializer
error in TS 4.8.4.
Hi, this is still causing a problem in the latest version of TS. "typescript": "^4.9.3"
const path = '/admin/refresh'
const enum AuthEndpoints {
login = '/admin/login',
refresh = `${path}` _//error: Computed values are not permitted in an enum with string valued members.ts(2553)_
}
export default AuthEndpoints
This PR is in TypeScript 5.0, not 4.9 (which was closed to new features after the beta). If you want to use it now, you can try the nightlies, but you won't see this in 4.9.
So, it seems TS5 still has the enum stomping problem
https://www.typescriptlang.org/play?ts=5.0.0-dev.20221121#code/MYewdgzgLgBARiAhrAvDATAbgFCkrAcyVRgEYcBTMAVwFsYA5cAFQEsKAnANgEkoLaAcQ6IADgAsAwiAAmFCDADe2GDACyIEBAow0CZABoVMQZu26YRQ8YAyZikYC+QA