The Typescript docs show the following example:
interface NumberDictionary {
[index: string]: number;
length: number; // ok, length is a number
Like this:
interface NumberDictionary {
[index: string]: number | string;
length: number;
name: string;
}
The problem is that such a type is inherently inconsistent. Consider the following code:
let prop = Math.random() > 0.5 ? "name" : "other"
let dic: NumberDictionary;
let value = dic[prop] // typed as number but could end up as string at run-time
The index definition tells us number
but we might end up with string
at runtime.
The honest thing to do is make the index signature return number | string
.
interface NumberDictionary {
[index: string]: number | string;
length: number;
name: string;
}
let prop = Math.random() > 0.5 ? "name" : "other"
let dic: NumberDictionary;
let value = dic[prop] // typed as number | string we need a type guard to tell teh difference
The honest solution might not always be practical, and, being fully aware of the dangers, you can define an intersection type that will let you get away with the inconsistency:
type NumberDictionary = {
[index: string]: number;
} & {
length: number;
name: string;
}
let prop = Math.random() > 0.5 ? "neverName" : "other"
let dic: NumberDictionary = {
name: "",
length: 1
} as NumberDictionary; // type assertion necessary, ts will still complain here about the inconsistentcy
let value = dic[prop] // typed as number, hope everyone avoids passing in name