Vyhledávání na webu

Jak vytvořit konstruktor třídy v TypeScriptu s objektem definujícím pole třídy?


V TypeScriptu je možné vytvořit třídu s konstruktorem, který přijímá parametr s přístupovými modifikátory, a ten automaticky převádí tyto parametry na pole třídy.

class Item {
  constructor(
    public id: number,
    public updatedAt: number,
    public createdAt: number,
  ) {}
}

const item = new Item(1, 1, 1);
item.id // 1

Přemýšlím, jestli existuje způsob, jak předat všechny tyto parametry místo toho v objektu

class Item {
  constructor({
    public id: number,
    public updatedAt: number,
    public createdAt: number,
  }) {}
}

const item = new Item({ id: 1, updatedAt: 1, createdAt: 1 });
item.id // 1

Je to možné? Bude to někdy možné?

Existují nějaké způsoby, jak udělat něco podobného?

Odpověď č. 1

Nejjednodušší způsob by byl deklarovat pole ve třídě a použít mapovaný typ jako parametr, poté použít Object.assign k přiřazení polí do this. Máme několik možností, který mapovaný typ použít:

Částečný<T>

Typ bude obsahovat všechny členy (pole a metody) třídy, ale všechny jsou nepovinné. Nevýhodou je, že nemůžeme některá pole učinit povinnými a volající může potenciálně přepsat metodu.

class Item {

    public id: number;
    public updatedAt: number;
    public createdAt: number;
    constructor(data: Partial<Item>) {
        Object.assign(this, data);
    }
    method() {}
}

//Works 
const item = new Item({ id: 1, updatedAt: 1, createdAt: 1 });
//This also works unfortunately 
const item2 = new Item({ id: 1, method() { console.log('overriden from param !')} });

Vybrat<T, K>

Tento mapovaný typ nám umožňuje vybrat některé vlastnosti z T tím, že specifikujeme sjednocení několika řetězcových literálových typů, které jsou klíči T. Výhodou je, že Pick zdědí, zda je pole povinné nebo ne, z původního deklarace ve třídě (takže některá pole mohou být povinná a jiná volitelná) a protože specifikujeme, které členy vybíráme, můžeme vynechat metody. Nevýhodou je, že musíme psát názvy vlastností dvakrát (jednou ve třídě a jednou v Pick):

class Item {
    public id: number;
    public updatedAt?: number;
    public createdAt?: number;
    constructor(data: Pick<Item, "id" | "updatedAt" | "createdAt">) {
        Object.assign(this, data);
    }
    method() {}
}
const item = new Item({ id: 1  }); //id is required others fields are not
const item2 = new Item({ id: 1, method() {}  }); // error method is not allowed

Vlastní namapovaný typ, který odstraňuje metody

Třetí možností by bylo vytvořit typ podobný Pick, který automaticky zahrnuje všechna pole třídy, ale ne metody. To můžeme udělat v TypeScriptu 2.8 pomocí podmíněných typů (v době psaní ještě nevydané, ale mělo by být vydáno v březnu 2018, nyní jej můžete získat přes npm install -g typescript@next). To má výhody Pick bez nutnosti znovu specifikovat názvy polí:

type NonMethodKeys<T> = {[P in keyof T]: T[P] extends Function ? never : P }[keyof T];  
type RemoveMethods<T> = Pick<T, NonMethodKeys<T>>; 

class Item {
    public id!: number;
    public updatedAt?: number;
    public createdAt?: number;
    constructor(data: RemoveMethods<Item>) { // No need to specify field names again
        Object.assign(this, data);
    }
    method() {}
}

const item = new Item({ id: 1  });  //id is required others fields are not
const item2 = new Item({ id: 1, method() {}  }); // error method is not allowed 

Odkaz na hřiště

Související články