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"?
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!
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.