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?

Answers:

Answer

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
Answer

Like this:

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

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us Javascript

©2020 All rights reserved.