Skip to content

Flag like enums as index signature types don't offer type guarantees #39753

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

Closed
mohkale opened this issue Jul 26, 2020 · 4 comments
Closed

Flag like enums as index signature types don't offer type guarantees #39753

mohkale opened this issue Jul 26, 2020 · 4 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@mohkale
Copy link

mohkale commented Jul 26, 2020

TypeScript Version: 4.0.0-dev.20200726

Search Terms: enum index pattern-matching

Code

const enum Foo {
    hello = 1 << 0, 
    world = 1 << 1, 
    buddy = 1 << 2,
}

const myFoos: { [key in Foo]: string } = {
  [Foo.hello]: "hello",
  [Foo.world]: "world",
}

console.log(myFoos)

Expected behavior:
The program fails to compile because property '4' is missing in myFoos

As a counter example, this fails to compile properly:

const enum Foo {
    hello = 0, 
    world = 1, 
    buddy = 2,
}

const myFoos: { [key in Foo]: string } = {
  [Foo.hello]: "hello",
  [Foo.world]: "world",
}

because Foo.buddy is missing in myFoos.

Actual behavior:
Succesfully compiles and outputs { '1': 'hello', '2': 'world' } even though one of the enum fields doesn't exist.

Playground Link: Link

Related Issues:

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Jul 27, 2020
@RyanCavanaugh
Copy link
Member

[key in Foo]: string (for the first example) resolves to [n: number]: string - a mapped type over a non-union doesn't produce an object with defined properties, and Foo is only a union (rather than a subtype of number) when its members are initialized with constant literals. Neither of those behaviors are really negotiable, even if their combination in this instance is a bit surprising.

@mohkale
Copy link
Author

mohkale commented Jul 27, 2020

@RyanCavanaugh So is there an alternative recommended approach here? I mean this is perfectly valid and offers type safety as well,

const enum Foo {
    hello, 
    world, 
    buddy,
}

const myFooFlags: { [key in Foo]: number } = {
  [Foo.hello]: 1 << 0,
  [Foo.world]: 1 << 2,
  [Foo.buddy]: 1 << 3,
}

So is the stance that we should create an auxiliary store for any flag variables and then pass (from the enum) through them instead? This feels too roundabout and cumbersome to be a solution though.

@AjiTae
Copy link

AjiTae commented Jul 27, 2020

Typescript just doesn't do the math.) You need const values.
If you are aiming for readability - try new binary syntax:

const enum Foo {
    hello = 0b0001, 
    world = 0b0010, 
    buddy = 0b0100,
}

const myFoos: { [key in Foo]: string } = {
  [Foo.hello]: "hello",
  [Foo.world]: "world",
}

console.log(myFoos)

@mohkale
Copy link
Author

mohkale commented Jul 27, 2020

Yep, did not know about the binary syntax, this fixes the issue completely. Thnx 👍

@mohkale mohkale closed this as completed Jul 27, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants