Združené typy generických a negenerických polí v TypeScripte


Práve teraz si hrám s TypeScriptom a skúšal som stránku Playground s myšlienkou na konverziu existujúceho projektu na TypeScript.

Riešil som problém, na ktorom som pracoval, ale počas toho som narazil na niečo, čo úplne nechápem. Ide o najjednoduchšie generické triedy front, čiastočne zobrazené tu.

class Queue<T> {
  private store: T[] = [];
  public enqueue(item: T) {
    this.store.push(item);
  }
}

const queue = new Queue<number>();
queue.enqueue(5);       // works
queue.enqueue('six')    // properly errors

To funguje samozrejme dobre. Funguje to tiež dobre, ak je podkladové úložisko typované ako zjednotenie dvoch typových premenných.

class Queue<T, U> {
  private store: (T | U)[] = [];
  public enqueue(item: T | U) {
    this.store.push(item);
  }
}

const queue = new Queue<number, boolean>();
queue.enqueue(5);       // works
queue.enqueue(true);    // works
queue.enqueue('six')    // properly errors

Avšak, keď nastavím store ako zjednotený typ typovej premennej a už existujúceho konkrétneho typu, kontrola typov nefunguje na enqueue. (Empty je trieda singleton, určená na napodobnenie môjho starého spôsobu indikácie prázdneho frontu vrátením konkrétneho objektu EMPTY; nie som si istý, čo je najlepší spôsob, ako to urobiť, ale to je otázka na inú tému.)

class Empty {
  // implementation of singleton class
}

const EMPTY = Empty.instance;

class Queue<T> {
  private store: (T | Empty)[] = [];
  public enqueue(item: T | Empty) {
    this.store.push(item);
  }
}

const queue = new Queue<number>();
queue.enqueue(5);       // works
queue.enqueue(EMPTY);   // works
queue.enqueue('six')    // also works, but it shouldn't

Zavedenie aliasu typu pre T | Empty nepomáha, čo nie je prekvapivé.

V skutočnosti to pre mňa nie je problém, pretože moja implementácia frontu ukladá iba typ T. Len moja dequeue má vo svojom podpise unijný typ, pretože vracia EMPTY, ak je úložisko prázdne (a to funguje dobre). Ale určite vidím miesto pre pole unijného typu s typovým parametrom a konkrétnym typom, a bez hlbokého skúmania sa zdá, že by to určite malo byť možné.

Je to niečo, čomu nerozumiem v jazyku, robím to zle, alebo je to len obmedzenie kompilátora?