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!