TypeScript
TypeScript copied to clipboard
TS 2535 when using const enum values derived from other const enum values
TypeScript Version: 3.7.2
Search Terms: 2535 const enum
Code
// A *self-contained* demonstration of the problem follows...
// Test this by running `tsc` on the command-line, rather than through another build tool such as Gulp, Webpack, etc.
const enum Foo { Value = 0x01 }
const enum Bar { Value = 0x02 }
const enum Baz { Value = Foo.Value | Bar.Value }
type Test = Baz.Value
const foo: Foo.Value = Foo.Value
const bar: Bar.Value = Bar.Value
const baz: Baz.Value = Baz.Value
Expected behavior: It to type-check without errors
Actual behavior: "Enum type 'Baz' has members with initializers that are not literals. (2535)" on both the type alias and the baz
type, referencing Baz.Value
in the type position.
Playground Link: https://www.typescriptlang.org/play/?ssl=1&ssc=1&pln=8&pc=33#code/MYewdgzgLgBApmArgWxgMRCGBvGA1AQwBtE4YBeGABgA8qBGGAXwChRJYEUYAhAgJxz5ipCtToAmZm3DR4SVHwBeQwiTKUMIAHRrRAH14DdIsqygBPAA5kAKnDmVlJ9SxkcYAM0wAudJhdRTQC9OHc5ACMBPz5+QI0jONDw2CilGIIleLFnUKA
Related Issues:
~~Accidentally filed this - please ignore.~~ Updated with intended text.
My immediate use case is that of using a discriminated union based on part of a bit mask. Something roughly like this, but on a bit larger of a scale:
export const enum Mask {
Type = 0x00FF,
IsStatic = 1 << 4,
}
export const enum Type {
One = 0x00,
Two = 0x01,
Three = 0x02,
}
export const enum MaybeStatic {
One = Type.One,
Two = Type.Two,
Three = Type.Three,
StaticOne = Type.One | Mask.IsStatic,
StaticTwo = Type.Two | Mask.IsStatic,
StaticThree = Type.Three | Mask.IsStatic,
}
export type Node = NodeOne | NodeTwo | NodeThree;
export interface NodeOne {
type: MaybeStatic.One | MaybeStatic.StaticOne;
value: string;
}
export interface NodeTwo {
type: MaybeStatic.Two | MaybeStatic.StaticTwo;
value: number;
}
export interface NodeThree {
type: MaybeStatic.Three | MaybeStatic.StaticThree;
value: symbol;
}
My workaround is specifying these directly, but this is very much so not ideal. (I'm trying to take advantage of TS's constant evaluation to avoid duplicating work with the compile-time constants.)
same problem, in my version 4.0.2
const enum A { a = 1 }
const enum B { b = A.a }
const a = A.a; // typeof 'a' is 'A.a'
const b = B.b; // typeof 'b' is 'B' rather than 'B.b'
that's because typescript requires only literal members of a const enum can be used as a type. but typescript regards B.b
's initializer A.a
as a computing expression rather than a literal, even though this computing expression can be computed in compile time. note that is literal is a stricter requirement than can be computed in compile time.
const enum A { a = 1 }
const enum B { b = A.a }
type Ta = A.a; // ok
type Tb = B.b; // error
actually I don't think it's a good convention to use a member of a const enum, like A.a
/B.b
, as a type, it has ambiguous semantics.
so there is a workaround:
const A = { a: 1 } as const;
type A = (typeof A)[keyof typeof A];
const B = { b: A.a } as const;
type B = (typeof B)[keyof typeof B];
const a = A.a; // ok, typeof 'a' is 1
const b = B.b; // ok, typeof 'b' is 1
In the TypeScript handbook about enums, it says that a reference to previously defined constant enum member (which can originate from a different enum)
is also consider a constant enum expression, which seems to contradict to this issue.
Maybe the (which can originate from a different enum)
part from the handbook is not implemented?
got this in such case:
const enum Foo {
a = 0,
b = 1 << 1,
c = 1 << 2,
all = a | b | c
}
resolved with using literals without any calculations
const enum Foo {
a = 0,
b = 1,
c = 2,
all = 3
}
@Walkalone13 Please file a new issue. This one's been closed for over a year.