Skip to content

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

New issue

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

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

Already on GitHub? # 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
# for free to join this conversation on GitHub. Already have an account? # 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