Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object index key type in Typescript

I defined my generic type as

interface IDictionary<TValue> {     [key: string|number]: TValue; } 

But TSLint's complaining. How am I supposed to define an object index type that can have either as key? I tried these as well but no luck.

interface IDictionary<TKey, TValue> {     [key: TKey]: TValue; }  interface IDictionary<TKey extends string|number, TValue> {     [key: TKey]: TValue; }  type IndexKey = string | number;  interface IDictionary<TValue> {     [key: IndexKey]: TValue; }  interface IDictionary<TKey extends IndexKey, TValue> {     [key: TKey]: TValue; } 

None of the above work.

So how then?

like image 917
Robert Koritnik Avatar asked Jan 26 '17 09:01

Robert Koritnik


People also ask

How do you define a type of object key in TypeScript?

Use the keyof typeof syntax to create a type from an object's keys, e.g. type Keys = keyof typeof person . The keyof typeof syntax returns a type that represents all of the object's keys as strings.

What is index type in TypeScript?

The indexing type is itself a type, so we can use unions, keyof , or other types entirely: type I1 = Person ["age" | "name"]; type I1 = string | number. type I2 = Person [keyof Person ]; type I2 = string | number | boolean.

How would you determine the key object type?

Use the typeof operator to get the type of an object or variable in JavaScript. The typeof operator also returns the object type created with the "new" keyword. As you can see in the above example, the typeof operator returns different types for a literal string and a string object.

What is key string in TypeScript?

The {[key: string]: string} syntax is an index signature in TypeScript and is used when we don't know all the names of a type's properties ahead of time, but know the shape of the values. The index signature in the examples means that when an the object is indexed with a string , it will return a string .


2 Answers

You can achieve that just by using a IDictionary<TValue> { [key: string]: TValue } since numeric values will be automatically converted to string.

Here is an example of usage:

interface IDictionary<TValue> {     [id: string]: TValue; }  class Test {     private dictionary: IDictionary<string>;      constructor() {        this.dictionary = {}        this.dictionary[9] = "numeric-index";        this.dictionary["10"] = "string-index"         console.log(this.dictionary["9"], this.dictionary[10]);     } } // result => "numeric-index string-index" 

As you can see string and numeric indices are interchangeable.

like image 153
Nickeat Avatar answered Oct 10 '22 20:10

Nickeat


In javascript the keys of object can only be strings (and in es6 symbols as well).
If you pass a number it gets converted into a string:

let o = {}; o[3] = "three"; console.log(Object.keys(o)); // ["3"] 

As you can see, you always get { [key: string]: TValue; }.

Typescript lets you define a map like so with numbers as keys:

type Dict = { [key: number]: string }; 

And the compiler will check that when assigning values you always pass a number as a key, but in runtime the keys in the object will be strings.

So you can either have { [key: number]: string } or { [key: string]: string } but not a union of string | number because of the following:

let d = {} as IDictionary<string>; d[3] = "1st three"; d["3"] = "2nd three"; 

You might expect d to have two different entries here, but in fact there's just one.

What you can do, is use a Map:

let m = new Map<number|string, string>(); m.set(3, "1st three"); m.set("3", "2nd three"); 

Here you will have two different entries.

like image 44
Nitzan Tomer Avatar answered Oct 10 '22 20:10

Nitzan Tomer