To bardziej wpis blogowy, niż lekcja, ale zachęcam do przeczytania. Mówię, czego mi brakuje osobiście w TypeScript.

Ok, rzućmy okiem na taki interfejs:


export interface Item {
    id: string,
    item: string,
    checked: boolean,
}

Teraz klasa:

export default class ListItem implements Item {

    constructor(
        private _id: string = '',
        private _item: string = '',
        private _checked: boolean = false,
    ) { }

    get id(): string {
        return this._id
    }

    set id(id: string) {
        this._id = id
    }

    get item(): string {
        return this._item
    }

    set item(item: string) {
        this._item = item
    }

    get checked(): boolean {
        return this._checked
    }

    set checked(checked: boolean) {
        this._checked = checked
    }
}

No mega długie to jest. Nawet mimo użycia constructor promotion. Po pierwsze, przydałby się generyczny setter i getter dla dwóch opcji:

  • Property, które istnieje
  • Property, które nie istnieje (odpowiednik __get i __set z PHP)

I dopiero później, aby móc to jakoś rozszerzać, jeżeli się chce, robić jakieś bardziej szczegółowe sprawdzenia i tak dalej. Jasne, mamy opcję z return new Proxy:

export default class ListItem implements Item {

    constructor(
        private _id: string = '',
        private _item: string = '',
        private _checked: boolean = false,
    ) { 
        return new Proxy(this, {
            get(){

            },
            set(){
                return true;
            }   
      

To tylko taki szkielet. No jasne, fajnie, można i tak, ale za dużo kombinowania. Przypominam, że jeżeli chcielibyśmy z wewnątrz klasy zrobić np. freeze, seal, prevent extensions albo defineProperty, to też tak w obiektówce robimy, w konstruktorze, this jako pierwszy argument, wtedy działamy na instancji obiektu.

Ok, druga rzecz, która by się przydała, to interfejs dla konstruktora/klasy. O takie coś mi chodzi:

export interface ListItemsProperties {
    private _id: string = '',
    private _item: string = '',
    private _checked: boolean = false,
}

export default class ListItem implements Item {

    constructor(ListItemsProperties)
    //(...)
}

Fajnie by było, żebyśmy mogli to sobie tak zdefiniować i potem wrzucić w konstruktor. Nie chodzi mi o DI, tylko o uproszczenie definiowania klasy. Można to oczywiście osiągać przez dziedziczenie, klasy abstrakcyjne i tak dalej.

Po prostu tak sobie myślę, że fajnie by było i taką mieć możliwość.

Swoją drogą, istnieje coś takiego jak interfejs konstruktora:

interface ConfigInterface {
  config: string;
}
interface InterfaceWithConsturctor {
  new(n: string): { config: string };
}
class Config implements ConfigInterface {
  public config: string;
  constructor (config: string) {
    this.config = config;
  }
}

Ale to ma zupełnie inne zastosowanie:

interface ConfigInterface {
  config: string;
}
interface InterfaceWithConsturctor {
  new(n: string): { config: string };
}
class Config implements ConfigInterface {
  public config: string;
  constructor (config: string) {
    this.config = config;
  }
}
function setTheState(n: InterfaceWithConsturctor) {
  return new n('{ state: { clicked: true, purchasedItems: true } }');
}
let configObj: Config = setTheState(Config);
console.log(configObj);
console.log(setTheState(Config).config);

Czyli mamy funkcję, która przyjmuje klasę (a zatem funkcję-konstruktor z odrobiną cukru składniowego ES6+) i zwraca nowy obiekt tej klasy. I teraz robimy interfejs, który pozwala nam otypować jakie klasy mogą być w ten sposób przyjmowane.

Taka ciekawostka, może się komuś przyda. Więcej TSa niedługo!