Singletonów ogólnie radzą unikać, natomiast w jednym projekcie zauważyłem singleton, który w dodatku był specyficzny, inaczej pomyślany, niż to w przykładach pokazują. Zobaczmy.

Ok, więc to jest taki typowy singleton w TS:

class Lonely {
  private static instance: Lonely;

  private constructor() {}

  static getInstance() {
    if (this.instance) {
      return this.instance;
    }
    this.instance = new Lonely();
    return this.instance;
  }
}

Prywatny konstruktor, statyczne publiczne getInstance. Gdyby jeszcze konstruktor musiał bazować na jakichś argumentach, to by się zrobiło createInstance i wtedy pola do popisu byśmy nie mieli.

Ale tutaj konstruktor jest pusty, więc ten przykład jest głupi (już pomijam, że w JS/TS dopisujemy do statycznych pól rzeczy przez this, nie np. static, mam nadzieję, że to nas nie dziwi, tak po prostu jest w JS).

Ok, to teraz zobaczmy taki interfejs i podpowiedź jest taka, że ten singleton będzie tylko ten interfejs implementował i pytanie jest, co możemy dzięki temu wywnioskować:

import ListItem from './ListItem'

interface List {
    list: ListItem[],
    load(): void,
    save(): void,
    clearList(): void,
    addItem(itemObj: ListItem): void,
    removeItem(id: string): void,
}

Wywnioskować możemy to, że klasa ma tylko jedno pole, list, które jest tablicą określonego typu, zapewne z wartością domyślną i także, posiada ta klasa metody, które służą do pracy na tej tablicy.

Zapewne zatem konstruktor będzie czymś takim, że z public static createInstance argumentów do private constructor przenosić nie będziemy.

To pytanie, po co nam w ogóle getInstance i createInstance? xD

No pytanie za sto punktów, a odpowiedź banalna:

export default class FullList implements List {

    static instance: FullList = new FullList()

    private constructor(private _list: ListItem[] = []) { }

    get list(): ListItem[] {
        return this._list
    }

    //(...)
}

Ha. Nic więcej nie trzeba. Prywatny konstruktor z domyślną wartością, pole statyczne instance, gdzie dajemy new FullList (korzystamy z konstruktora), plus implementacja metod z interfejsu (jedyną property, jaką jest list już zaimplementowaliśmy, nawet getter do niej).

I tyle. Potem po prostu importujemy klasę i odnosimy się do FullList.instance, nie musząc nigdzie tego tworzyć. Fajna sztuczka, nie znałem tego.

Więcej TSa niedługo.