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 })
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 } .
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.
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 .
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.
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With