TypeScript Generics: Create a generic map
It took me a while to understand the way getItem() works in its current implementation. I even thought that there's a bug (after rereading the code, I see I was wrong about it).


Let's start with obvious. The ret variable; can be safely removed.
The whole search loop is counter-intuitive to me, because each element in the items array is an object which stores a value: T under a different key: string. If you really want to use items: object[], you'd rather have it's elements declared as { key: string, value: T }. And this immediately moves us to the next point that answers your question about the choice of the type of the backing object.
An entity that stores key-value pairs in TypeScript and JavaScript is Object. Why not use it instead? I don't know if using Object is disallowed by your real problem definition.

If it is not disallowed, it would be very natural to define items as an Object and wrap it into a GenericMap which exposes the API your problem requires.

However, if it is disallowed, you should not use it in the setItem()'s code (tmp[key] = item;) as well. Because it's technically same kind of a "cheat" anyway.


Below is the code that is built around an Object instead of an Array. As you can see, pretty much all of the operations are now "pass-through" one liners. Including the getItem() which you said got pretty long in the original code.



class GenericMap&lt;T extends number | string&gt; {
  private items: { [key: string]: T } = { };

  setItem(key: string, item: T): void {
    this.items[key] = item;
  }

  getItem(key: string): T {
    return this.items[key];
  }

  clear(): void {
    this.items = {};
  }

  printMap(): string {
    const itemsAsString = Object
      .getOwnPropertyNames(this.items)
      .map(key =&gt; `'${key}': ${this.getItem(key)}`)
      .join(", ");
    return `{ ${itemsAsString} }`;
  }
}


A few TypeScript style side notes:


private items: { [key: string]: T } = { };


If we initialize our fields in-place (as a part of their definition), we don't need an explicit constructor() which makes code shorter.
{ [key: string]: T } is a compiler hint that will -- at least in some cases -- help detecting incorrect usages of the field

I personally have never seen the whitespaces around &lt; and &gt; when used with generics, e.g. GenericMap &lt; T extends number | string &gt;. It's not TypeScript idiomatic I believe.


Hope, it helps.