Dictionary in Typescript based on arrays of generic types.

Typescript is definitely a promising language (at the time of writing the last release is 1.6). Unfortunately there are still some missing facilities. This is especially true for those who are used to work with strongly typed programming languages like C#.

One of this “missing facilities” is the dictionary type.

Unless you go with ECMAScript 6 and its map object, the classical solution is to use an object as a map:

map: { [key: string]: U } = { };

Instead of this, I’ve decided to take a different approach and to build my own dictionary type.


Class definition

export class Dictionary<T extends number | string, U>{
        private _keys: T[] = [];
        private _values: U[] = [];
    
    ...

}

As you can see from the code above, I’ve done this by simply creating a generic class with two private fields:

  • an array of T for the keys and
  • an array of U for the values.

Please note the constraint on T:

T extends number | string

In this way the type of T can’t be any object: it’s restricted to string or number. The reasons for that are mainly two.

  • In most cases the keys of a dictionary are strings or integers.
  • In the implementation of the usual methods to interact with a dictionary (such as remove, containsKey,…), I’ve used the array’s method indexOf(). In order to find elements, this method uses strict equality. Therefore, allowing to use any kind of object as key, it would have meant, for example, that in order to remove an element, we would have to provide a reference to the key object of the item to remove. This makes no sense. (For more information about strict equality please refer to “The Strict Equality Comparison Algorithm” of ECMAScript® Language Specification.)


Implementation

For what concern the implementation, I’ve coded the following methods:

add(key: T, value: U): void

remove(key: T): boolean

getValue(key: T): U

containsKey(key: T): boolean

changeValueForKey(key: T, newValue: U): void

keys(): T[]

values(): U[]

count(): number

I’m not going into details for every single method: they are quite straightforward. However, I’d like to point out one thing. While I was coding, I realized that most of these methods perform exactly the same two steps:

  • check if the key is valid (i.e not null or undefined)
  • execute an action (for example “add a new idem”)

So I decided to centralize this generic behavior in the following private method:

        private checkKeyAndPerformAction(action: { (key: T, value?: U): void | U | boolean }, key: T, value?: U): void | U | boolean {

            if (this.isEitherUndefinedNullOrStringEmpty(key)) {
                throw new Error(this.undefinedKeyErrorMessage);
            }

            return action(key, value);
        }



Its task is to check key’s validity, and if so, to invoke a function (provided as method’s parameter) that encloses the action to perform (for example “adding the new item to the dictionary”).
In this way, in the implementation of the above public API methods, I simply have to define the action to perform and call checkKeyAndPerformAction without having to worry about an undefined/empty key.
To better understand this, please look at the code of add method:

        public add(key: T, value: U): void {

            var addAction = (key: T, value: U): void => {
                if (this.containsKey(key)) {
                    throw new Error("An element with the same key already exists in the dictionary.");
                }

                this._keys.push(key);
                this._values.push(value);
            };

            this.checkKeyAndPerformAction(addAction, key, value);
        }


Closing remarks

If you are interested in using my dictionary’s implementation (or you are just curious) you can find the full source code here.
There you will find, in addition to the full dictionary’s class, also a battery of unit tests developed using the Jasmine framework. (Note: to correctly run the Visual Studio project you need to set SpecRunner.html as start page.)

Final remark: if you use ReSharper you will see that it complains about the key’s type defined in the dictionary’s initialization. If you define a new dictionary as follow, i.e. choosing the keys to be a string

var dict = new Collections.Dictionary<string, string>();

ReSharper will warn you telling that “string does not extends string”. In fact, it’s not entirely wrong, but…
Do not worry too much about that! Everything works properly: typescript’s class will compile in JavaScript without any issues.

That’s all for now!
For any feedback please feel free to leave a comment or contact me.