170

I'm trying to implement a hashmap/dictionary interface. So far I have:

export interface IHash {
    [details: string] : string;
} 

I'm having some trouble understanding what exactly this syntax means. If I were to do var x : IHash = {}; how would I go about adding/accessing the data?

starball
  • 20,030
  • 7
  • 43
  • 238
mysticalstick
  • 2,479
  • 5
  • 16
  • 21

3 Answers3

214

Just as a normal js object:

let myhash: IHash = {};   

myhash["somestring"] = "value"; //set

let value = myhash["somestring"]; //get

There are two things you're doing with [indexer: string] : string

  • tell TypeScript that the object can have any string-based key
  • that for all key entries the value MUST be a string type.

enter image description here

You can make a general dictionary with explicitly typed fields by using [key: string]: any;

enter image description here

e.g. age must be number, while name must be a string - both are required. Any implicit field can be any type of value.

As an alternative, there is a Map class:

let map = new Map<object, string>(); 

let key = new Object();

map.set(key, "value");
map.get(key); // return "value"

This allows you have any Object instance (not just number/string) as the key.

Although its relatively new so you may have to polyfill it if you target old systems.

Meirion Hughes
  • 24,994
  • 12
  • 71
  • 122
  • 1
    "unless explicitly typed otherwise" - and how it could be stated? AFAIK, any field stated explicitly must conform to index signature. – Cerberus Oct 25 '18 at 06:25
  • huh... you're totally right. either I confused it with typing it as `any` or they they changed that behavior. I'd wager the former. Thanks. :) – Meirion Hughes Oct 25 '18 at 07:52
  • With the latest TS (4.7), there's one serious problem with the first approach. If I type it like this, then for any `key` of type `string` TS will think that `myhash[key]` is a `string`, which is obviously not the case. This is actually quite surprising because with this interpretation there's literally no "normal" object (unless one uses `Proxy`) which implements this interface. – Tomasz Lenarcik Aug 26 '22 at 14:49
102

The most simple and the correct way is to use Record type Record<string, string>

const myVar : Record<string, string> = {
   key1: 'val1',
   key2: 'val2',
}
Wilfred Hughes
  • 29,846
  • 15
  • 139
  • 192
aWebDeveloper
  • 36,687
  • 39
  • 170
  • 242
10
var x : IHash = {};
x['key1'] = 'value1';
x['key2'] = 'value2';

console.log(x['key1']);
// outputs value1

console.log(x['key2']);
// outputs value2

If you would like to then iterate through your dictionary, you can use.

Object.keys(x).forEach((key) => {console.log(x[key])});

Object.keys returns all the properties of an object, so it works nicely for returning all the values from dictionary styled objects.

You also mentioned a hashmap in your question, the above definition is for a dictionary style interface. Therefore the keys will be unique, but the values will not.

You could use it like a hashset by just assigning the same value to the key and its value.

if you wanted the keys to be unique and with potentially different values, then you just have to check if the key exists on the object before adding to it.

var valueToAdd = 'one';
if(!x[valueToAdd])
   x[valueToAdd] = valueToAdd;

or you could build your own class to act as a hashset of sorts.

Class HashSet{
  private var keys: IHash = {};
  private var values: string[] = [];

  public Add(key: string){
    if(!keys[key]){
      values.push(key);
      keys[key] = key;
    }
  }

  public GetValues(){
    // slicing the array will return it by value so users cannot accidentally
    // start playing around with your array
    return values.slice();
  }
}
C Smith
  • 222
  • 1
  • 6