Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript - How to represent an index signature as a generic type

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 })
like image 623
Matthew Layton Avatar asked Feb 10 '17 09:02

Matthew Layton


People also ask

How do I create a index signature in TypeScript?

Index signature syntax The syntax of an index signature is pretty simple and looks similar to the syntax of a property, but with one difference. Instead of the property name, you simply write the type of the key inside the square brackets: { [key: KeyType]: ValueType } .

How do you define a generic type in TypeScript?

Generics allow creating 'type variables' which can be used to create classes, functions & type aliases that don't need to explicitly define the types that they use. Generics makes it easier to write reusable code.

Which type string has no matching index signature?

The error "No index signature with a parameter of type 'string' was found on type" occurs when we use a value of type string to index an object with specific keys. To solve the error, type the string as one of the object's keys using keyof typeof obj .

Is incompatible with index signature?

The error "Property is incompatible with index signature" occurs when a property is not compatible with the specified type of the index signature. To solve the error, change the type of the property or use a union to update the type in the index signature.


Video Answer


2 Answers

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

like image 165
robkuz Avatar answered Oct 29 '22 05:10

robkuz


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

like image 1
David Nuñez Avatar answered Oct 29 '22 05:10

David Nuñez