Typescript で Map のキーをタプルにしたいとき 

# Typescript で Map のキーをタプルにしたいとき 
自分用メモ. Typescript 使っていると, Map オブジェクトのキーにタプルを指定するとうまく動かなかったので
調べてみると, どうやらキー値の比較に `===` を使っているのでダメなよう. 
とりあえず, これができないと困るので以下のような簡易 Map を実装した. 
そのうち Map の残りのメソッドに対応する・・かも.

```typescript
class TupleMap {
  private _values: Object = {};
  protected _clear(): void {
    this._values = {};
  }
  protected _get(key: Array<any>): any {
    if (key == null || key.length == 0) {
      return false;
    }
    let result: any = this._values[key[0]];
    for (let i = 1; i < key.length; i++) {
      if (result == null) {
        break;
      }
      result = result[key[i]];
    }
    return result;
  }
  protected _has(key: Array<any>): boolean {
    return this._get(key) != null;
  }
  private _zip<T1, T2>(a1: T1[], a2: T2[]): [T1, T2][] {
    let result: [T1, T2][] = [];
    for (let i = 0; i < a1.length; i++) {
      result.push([a1[i], a2[i]]);
    }
    return result;
  }
  protected _delete(key: Array<any>): boolean {
    const hasResult = this._has(key);
    if (!hasResult) {
      return false;
    }
    let stack: Object[] = [this._values];
    let result: any = this._values[key[0]];
    for (let i = 1; i < key.length; i++) {
      stack.push(result);
      result = result[key[i]];
    }
    // 削除
    let objs = this._zip(key, stack).reverse();
    delete objs[0][1][objs[0][0]]; // リーフノードは問答無用で削除
    for (let [k, o] of objs.slice(1, objs.length)) {
      // キーに対応するオブジェクトが空の場合には削除
      if (Object.keys(o[k]).length == 0) {
        delete o[k];
      } else {
        break;
      }
    }

    return true;
  }
  protected _set(key: Array<any>, value: any): this {
    if (key == null || key.length == 0) {
      throw new Error("invalid key given");
    }
    if (!(key[0] in this._values)) {
      this._values[key[0]] = {};
    }
    let obj = this._values[key[0]];
    for (let part of key.slice(1, key.length)) {
      if (!(part in obj)) {
        obj[part] = {};
      }
      obj = obj[part];
    }
    obj[key[key.length - 1]] = value;
    return this;
  }
}

interface TupleInterface<T, U> {
  clear(): void;
  get(key: T): U;
  has(key: T): boolean;
  delete(key: T): boolean;
  set(key: T, value: U): this;
}

export class Tuple2Map<T1, T2, U> extends TupleMap
  implements TupleInterface<[T1, T2], U> {
  public clear(): void {
    this._clear();
  }
  public get(key: [T1, T2]): U {
    return this._get(key);
  }
  public has(key: [T1, T2]): boolean {
    return this._has(key);
  }
  public delete(key: [T1, T2]): boolean {
    return this._delete(key);
  }
  public set(key: [T1, T2], value: U): this {
    return this._set(key, value);
  }
}

export class Tuple3Map<T1, T2, T3, U> extends TupleMap
  implements TupleInterface<[T1, T2, T3], U> {
  public clear(): void {
    this._clear();
  }
  public get(key: [T1, T2, T3]): U {
    return this._get(key);
  }
  public has(key: [T1, T2, T3]): boolean {
    return this._has(key);
  }
  public delete(key: [T1, T2, T3]): boolean {
    return this._delete(key);
  }
  public set(key: [T1, T2, T3], value: U): this {
    return this._set(key, value);
  }
}

export class Tuple4Map<T1, T2, T3, T4, U> extends TupleMap
  implements TupleInterface<[T1, T2, T3, T4], U> {
  public clear(): void {
    this._clear();
  }
  public get(key: [T1, T2, T3, T4]): U {
    return this._get(key);
  }
  public has(key: [T1, T2, T3, T4]): boolean {
    return this._has(key);
  }
  public delete(key: [T1, T2, T3, T4]): boolean {
    return this._delete(key);
  }
  public set(key: [T1, T2, T3, T4], value: U): this {
    return this._set(key, value);
  }
}
export class Tuple5Map<T1, T2, T3, T4, T5, U> extends TupleMap
  implements TupleInterface<[T1, T2, T3, T4, T5], U> {
  public clear(): void {
    this._clear();
  }
  public get(key: [T1, T2, T3, T4, T5]): U {
    return this._get(key);
  }
  public has(key: [T1, T2, T3, T4, T5]): boolean {
    return this._has(key);
  }
  public delete(key: [T1, T2, T3, T4, T5]): boolean {
    return this._delete(key);
  }
  public set(key: [T1, T2, T3, T4, T5], value: U): this {
    return this._set(key, value);
  }
}
```