Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
dead-claudia opened this issue Dec 27, 2019 · 6 comments · Fixed by #50528
Closed

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

dead-claudia opened this issue Dec 27, 2019 · 6 comments · Fixed by #50528
Labels
Fix Available A PR has been opened for this issue In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@dead-claudia
Copy link

dead-claudia commented Dec 27, 2019

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
Copy link
Author

dead-claudia commented Dec 27, 2019

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

@dead-claudia dead-claudia changed the title 2535 TS 2535 when using const enum values derived from other const enum values Dec 27, 2019
@dead-claudia dead-claudia reopened this Dec 27, 2019
@dead-claudia
Copy link
Author

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

@RyanCavanaugh RyanCavanaugh added In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Jan 13, 2020
@zimtsui
Copy link

zimtsui commented Dec 23, 2020

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

@peter50216
Copy link

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?

@typescript-bot typescript-bot added the Fix Available A PR has been opened for this issue label Oct 17, 2022
@Walkalone13
Copy link

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
}

@dead-claudia
Copy link
Author

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

@microsoft microsoft locked as resolved and limited conversation to collaborators Oct 23, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Fix Available A PR has been opened for this issue In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants