Typescript: String enum can't be used to index into an object

Created on 27 Jun 2017  ·  6Comments  ·  Source: microsoft/TypeScript

TypeScript Version: 2.4.0

Code

enum Name {
    A = "a",
    B = "b",
    C = "c",
}

// interfaces with computed property names are not supported yet, see https://github.com/Microsoft/TypeScript/issues/5579
interface Item {
    /* [Name.A] */ a: string;
    /* [Name.B] */ b: number;
    /* [Name.C] */ c: boolean;
}

const names: Name[] = [Name.A, Name.B, Name.C];

const item: Item = {
    [Name.A]: "a",
    [Name.B]: 1,
    [Name.C]: true,
};

names.forEach((name: Name) => {
    console.log(item[name]);
});

Expected behavior:
No errors are thrown (ideally also when using the commented code with computed interface property names).

Actual behavior:

src/test.ts(16,7): error TS2322: Type '{ [x: string]: string | number | boolean; }' is not assignable to type 'Item'.
  Property 'a' is missing in type '{ [x: string]: string | number | boolean; }'.
src/test.ts(23,17): error TS7017: Element implicitly has an 'any' type because type 'Item' has no index signature.
Bug Fixed

Most helpful comment

We do handle union types specially in indexing, we should probably do the same for string enums, since they are strictly a union of string literals (and possibly for all union enums).
I would say the change should be as simple as adding a clause to the if statement in getIndexedAccessType.

All 6 comments

The first error is duplicate of #16687, the second error is new.

I'm not sure if there's a way around the second one other than asserting as keyof typeof item. However, it _should_ work if Name is used for a mapped type definition, or when it's used in a computed key once #5579 works.

just to jump in with related question, what about

enum Name {
  A = 'a',
  B = 'b'
};

const item: Item = {
  [name: Name]: string; 
};

gives an error

Error:(6, 38) TS1023:An index signature parameter type must be 'string' or 'number'.

could it be possible to use the above approach to expect the property names to use only values defined in an enum Name

We do handle union types specially in indexing, we should probably do the same for string enums, since they are strictly a union of string literals (and possibly for all union enums).
I would say the change should be as simple as adding a clause to the if statement in getIndexedAccessType.

Given that #16687 already tracks the first part of this, let's use this bug to track the last part, the use of a string enum to index into an object type that has the same keys. Here's a cut-down repro:

```ts
// @noImplicitAny: true
enum Name {
A = "a",
B = "b",
C = "c",
}

interface Item {
a: string;
b: number;
c: boolean;
}

declare const item: Item;
declare const name: Name;
let result = item[name];

Fix is up at #18029

Was this page helpful?
0 / 5 - 0 ratings

Related issues

uber5001 picture uber5001  ·  3Comments

DanielRosenwasser picture DanielRosenwasser  ·  3Comments

MartynasZilinskas picture MartynasZilinskas  ·  3Comments

manekinekko picture manekinekko  ·  3Comments

fwanicka picture fwanicka  ·  3Comments