Skip to content

Type of reverse mappings of numeric enums is a string instead of keyof typeof Enum #38806

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

Open
Drundia opened this issue May 27, 2020 · 6 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@Drundia
Copy link

Drundia commented May 27, 2020

TypeScript Version: 4.0.0-dev.20200526

Search Terms:
enum
numeric enum
reverse enum

Code

enum Enum {
  one = 1,
  two = 2,
}

const a: keyof typeof Enum = Enum[Enum.one]; // (1)
const b = Enum[0]; // (2)
console.log(a, b);

Expected behavior:
When a numeric enum is indexed by itself the value should be of type keyof typeof Enum:
No error on line (1).
Error Property '0' does not exist on type 'typeof Enum' on line (2).

Actual behavior:
When a numeric enum is indexed by a number (including itself) the value is of type string:
Error Type 'string' is not assignable to type '"one" | "two"' on line (1).
No error on line (2).

Playground Link: Provided

Related Issues:

@RyanCavanaugh RyanCavanaugh added Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript labels Jun 4, 2020
@markm77
Copy link

markm77 commented Jul 27, 2020

I can certainly agree that enum reverse mappings should have type keyof typeof Enum rather than string. This loses information and can necessitate an unnecessary cast.

@nsotelo
Copy link

nsotelo commented Aug 20, 2020

I've been trying to make something like this work, but I am blocked by the same (or similar) issue. Would be really useful to not have the type loosened.

enum Allowed {
  A = 'a',
  B = 'b',
  C = 'c',
}

const values: { [key in Allowed]: boolean } = { a: true, b: false, c: true }
const filteredValues = Object.keys(values).filter((value) => values[value])

Expected behaviour:
Get the result const filteredValues = ['a', 'c']

Actual behaviour:
Get the error: 'string' can't be used to index type '{ a: boolean; b: boolean; c: boolean; }'.

Playground link: Here

@niedzielski
Copy link

I think enumerations are challenging to use well. For example, number enumerations contain reverse mappings:

enum Enum {
  one = 1,
  two = 2,
}

console.log(Object.values(Enum)) // Prints ["one", "two", 1, 2]

However, the typing doesn't reflect that:

type EnumKey = keyof typeof Enum // "one" | "two" but not the reverse mapping.
type EnumVal = typeof Enum[EnumKey]

const foo: EnumVal = 1234 // Invalid unchecked.
const bar: EnumKey = 'one' // Valid and allowed.
const baz: EnumKey = 1 // Valid and forbidden.

The problem worsens with declaration merging:

enum Color {
  Red,
  Green,
  Blue
}

namespace Color {
  export function size(): number {
    return Object.keys(Color).length
  }
}

console.log(Color.size()) // Prints 7 not 6.

Plain objects are less idiomatic and succinct but they behave as expected and a reverse mapping is optional and explicit:

const Enum = <const>{
  one: 1,
  two: 2,
  1: 'one',
  2: 'two'
}
type Enum = typeof Enum[keyof typeof Enum]

console.log(Object.values(Enum)) // Prints ["one", "two", 1, 2].

type EnumKey = keyof typeof Enum // 1 | 2 | "one" | "two"
type EnumVal = typeof Enum[EnumKey] // 1 | 2 | "one" | "two"

const foo: EnumVal = 1234 // Invalid and forbidden.
const bar: EnumKey = 'one' // Valid and allowed.
const baz: EnumKey = 1 // Valid and allowed.

@majellin24
Copy link

I would like to see this feature as well

@Xriuk
Copy link

Xriuk commented Mar 31, 2022

The problem persist, if I have a string variable and get an enum with reverse mapping the inferred type is still string, enclosing it in a parseInt() solves the issue

@jfw225
Copy link

jfw225 commented Sep 23, 2022

Found a workaround to the problem. I mention it in #50933.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

8 participants