maxedison
maxedison

Reputation: 17553

Typescript object indexer and key that doesn't match indexer type

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

Answers (2)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

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

Robbie Milejczak
Robbie Milejczak

Reputation: 5770

Like this:

interface NumberDictionary {
    [index: string]: number | string;
    length: number;
    name: string;
}

Upvotes: 2

Related Questions