18

Index signatures in TypeScript are defined thus:

Dictionary

[key: string]: T

Array

[index: number]: T

These could be wrapped into some simple, reusable types:

type DictionaryIndex<T> = {
    [key: string]: T
}

type ArrayIndex<T> = {
    [index: number]: T
}

Now I want to wrap these into a single type. I tried this:

type Index<TKey extends string|number, TValue> = {
    [key: TKey]: TValue
}

This does not compile due to the following error:

An index signature parameter must be of type 'string' or 'number'.

Is this not possible?

What the hell for?

Because

foo(obj: Index<string, Bar>)
foo(obj: Index<string, Bar> & Fooable<string>)

looks neater than

foo(obj: { [key: string]: Bar })
foo(obj: { [key: string]: Bar, canFoo: (foo: string) => Bar })
Matthew Layton
  • 39,871
  • 52
  • 185
  • 313
  • 2
    There is an issue on the TypeScript repo about that: https://github.com/Microsoft/TypeScript/issues/13398 Guess it is not possible yet. – Sebastian Sebald Feb 10 '17 at 10:29

2 Answers2

5

cool question!
I think the reason is that this is really an edge case for the compiler that has not been implemented probably because its not worth the effort.

index property keys can only be numbers that is obvious and object property keys can only be string or numbers. So at the end your constraint only states what is fact anyways and what would need to be handled by the compiler via a special case.
So again I assume that Saint Anders ditched that effort ;-)

but why not do this

type StrIndex<TValue> = {
    [key: string]: TValue
}

type NumIndex<TValue> = {
    [key: number]: TValue
}

foo(obj: StrIndex<Bar>)
foo(obj: StrIndex<Bar> & Fooable<string>)

It is not as neat as your solution but as a compromise it seems OK specifically as the set of XXXIndex types is limited to 2

robkuz
  • 9,488
  • 5
  • 29
  • 50
2

One alternative solution is to use the Record utility.

foo(obj: Record<string, Bar>)

https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type