Reputation: 17553
The Typescript docs show the following example:
interface NumberDictionary {
[index: string]: number;
length: number; // ok, length is a number
name: string; // error, the type of 'name' is not a subtype of the indexer
}
What is the recommended workaround for the above example, e.g. I have an object that I know has a name: string
property, and it can have any other possible keys, all of which must be numbers?
Upvotes: 3
Views: 123
Reputation: 249486
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
Upvotes: 2
Reputation: 5770
Like this:
interface NumberDictionary {
[index: string]: number | string;
length: number;
name: string;
}
Upvotes: 2