TypeScript icon indicating copy to clipboard operation
TypeScript copied to clipboard

TS 2535 when using const enum values derived from other const enum values

Open dead-claudia opened this issue 5 years ago • 4 comments

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:

dead-claudia avatar Dec 27 '19 17:12 dead-claudia

~~Accidentally filed this - please ignore.~~ Updated with intended text.

dead-claudia avatar Dec 27 '19 17:12 dead-claudia

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.)

dead-claudia avatar Dec 27 '19 20:12 dead-claudia

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

zimtsui avatar Dec 23 '20 16:12 zimtsui

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?

peter50216 avatar Jan 11 '22 03:01 peter50216

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 avatar Oct 19 '23 10:10 Walkalone13

@Walkalone13 Please file a new issue. This one's been closed for over a year.

dead-claudia avatar Oct 19 '23 20:10 dead-claudia