TypeScript: Enums can not be used for index signature types

TypeScript Version: 2.1.4

export interface UserInterfaceColors {
    [index in UserInterfaceElement]: ColorInfo;
}
export interface ColorInfo {
    r: number;
    g: number;
    b: number;
    a: number;
}
export enum UserInterfaceElement {
    ActiveTitleBar = 0,
    InactiveTitleBar = 1,
}

Expected behavior: No errors will be thrown.

Actual behavior: index in UserInterfaceElement throws the error Type 'UserInterfaceElement' is not assignable to type 'string'

This is a regression caused by #12425. The change makes sense, but there are very valid use cases for supporting enums in index signatures.

For another use case, in a project I’m working on, we use an enum to list all types of items. We have a function that takes an object with the enum values as keys, and data as values.

function loadItemData (data: { [index in ItemType]: string }): void;

A workaround is to use index: number, but then it’s not enforced that you have to use a valid ItemType as a key.

Original Issue: #2491

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 98
  • Comments: 21 (4 by maintainers)

Most upvoted comments

enum Test {
    A = "a",
    B = "b"
}
type PhaseApi1 = {
    [x in Test]: () => void;
};

@mhegazy Any reason that this works for type and not interface? I know it’s easy to use one over the other but it seems a strange restriction.

😭 <---- I need it in the Reactions

It’s unlikely that this happens to anyone else but me, but if you’re trying to figure out why the fix isn’t working for you, check that you spelled your enum correctly.

Approved.

Note that this will not cause nominal enforcement of enum index expressions!

Specifically, [index in UserInterfaceElement]: ColorInfo; will eagerly reduce to "0" | "1" : ColorInfo, so indexing by any other 0- or 1-valued enum value will not cause an error.

can we allow string & Whatever for index signatures too?

Fix is up at #18346, although I think we’ll need to discuss this at a design meeting before merging it.

@yordis Not sure, but looks like it got bumped back to 2.8, so probably at least another 3 months.

I’m not sure why and how but it started working for me (2.7.1).

So a long awaited (by me 😉 feature of enum exhaustive checking finally works.

Given

enum X {
  Y = 'y',
  Z = 'z',
}

type A = { [TKey in X]: string };

the following is ok

const a: A = {
  [X.Y]: 'x',
  [X.Z]: 'x',
}

but this is compile time error

const a: A = {
  [X.Y]: 'x',
}

@kg-currenxie

type PhaseApi1 = {
    [x in Test]?: () => void;
};

@augusto-moura Looks like it’s been moved from milestone TypeScript 2.6 to TypeScript 2.7 as seen above 😞

       const enum UnitType {
              PLAYER = 0x01,
              MONSTER = 0x02
       }
       const descriptor = ({
            [UnitType.PLAYER as number]: Player.descriptor,
            [UnitType.MONSTER as number]: Monster.descriptor
        })[type]

Doing it like so may help some in the interim

So, this issue isn’t actually fixed right? #18346 was closed without merging

There’s any ongoing work for fixing it?

Just a postmortem FYI for anyone who’s gone through what I did, trying for hours to solve this with all manner of type rejigging to remove index signature errors… Make sure your IDE linter has the latest version of Typescript. Those errors might not actually be errors at all.

+1 for the 😭 reaction option.

enum Test {
    A = "a",
    B = "b"
}
type PhaseApi1 = {
    [x in Test]: () => void;
};

Is there a way to make either key optional?

@kompot That’s because you’re mapping the enums to string values. You’re really just using strings as keys. This issue is for the use of numeric enum entries (which would allow auto-assigned enums to work as well)

As far as I know, it’s worked via string since in and string enums were both present in the language.