= { [index: TKey]: TValue }...">

Can generic TypeScript constraints provide "allowed" types?

Given the following code ...

type Indexable<TKey, TValue> = { [index: TKey]: TValue } 

This results in the following error:

The type of the index signature parameter must be "string" or "number".

Is there a way to limit TKey as a "string" or a "number"?

+7
generics typescript
source share
2 answers

As @ TitianCernicova-Dragomir points out, you cannot use TKey as a type in an index signature, even if it is equivalent to string or number .

If you know that TKey is exactly a string or number , you can simply use it directly and not specify TKey in your type:

 type StringIndexable<TValue> = { [index: string]: TValue } type NumberIndexable<TValue> = { [index: number]: TValue } 

Close by . It turns out that in a bunch of places in TypeScript, the index type for the properties should be string , not number . In such places, number usually regarded as a kind of subtype of string . This is because in JavaScript, indexes are converted to string anyway when you use them, which leads to this behavior:

 const a = { 0: "hello" }; console.log(a[0]); // outputs "hello" console.log(a['0']) // *still* outputs "hello" 

Since you cannot use number in the future, I will ignore it; if you need to use numeric keys, TypeScript will probably allow you, or you can convert to string manually. Back to the rest of the answer:


If you want TKey be more specific than string , i.e. only certain keys are allowed, you can use the associated types :

 type Indexable<TKey extends string, TValue> = { [K in TKey]: TValue } 

You would use it by passing a string literal or a string literal TKey for TKey :

 type NumNames = 'zero' | 'one' | 'two'; const nums: Indexable<NumNames, number> = { zero: 0, one: 1, two: 2 }; type NumNumerals = '0' | '1' | '2'; const numerals: Indexable<NumNumerals, number> = {0: 0, 1: 1, 2: 2}; 

And if you don't want to limit the key to specific literals or literal associations, you can still use string as TKey :

 const anyNums: Indexable<string, number> = { uno: 1, zwei: 2, trois: 3 }; 

Actually this definition for Indexable<TKey, TValue> so useful, it already exists in the TypeScript standard library as Record<K,T>

 type NumNames = 'zero' | 'one' | 'two'; const nums: Record<NumNames, number> = { zero: 0, one: 1, two: 2 }; 

Therefore, I recommend that you use Record<K,T> for these purposes, as these are standard and other TypeScript developers who read your code, are more likely to be familiar with it.


Hope this helps; good luck!

+3
source share

You can limit the output of TKey from a string or number (using extends), but this will not satisfy the compiler. index must be either a number or a string, not a generic type or any other type. This is described in the language profile.

+5
source share

All Articles