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?
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.
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 number
s 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.