data.mdx.frontmatter.hero_image

TypeScript – Implementacja interfejsu konstruktora

2018-08-16 | Frontend, TypeScript | bd90

Rozpoczynając swoją aktualną pracę w pewien sposób pożegnałem się z tematami frontend-owymi. Wiadomo, nie postawiłem grubej, czerwonej, nieprzekraczalnej linii, po prostu zajmuje się tym zdecydowanie rzadziej. Ostatnio jednak natrafiłem na zagadnienie, które zdecydowanie mnie zainteresowało, mianowicie tworzenie interfejsu dla konstruktora klasy w TypeScript. Od razu zaznaczam, że nie będę w tym artykule omawiał konfiguracji środowiska pod aplikację. Jeżeli to wasze pierwsze spotkanie z tą technologią polecam wam:

Interfejsy takie piękne

Od pierwszych chwil, kiedy zacząłem pracować z TypeScript-em, spodobało mi się pisanie interfejsów. Wiecie, niektórzy lubią szybkie samochody, inni interfejsy. Pozwalają hermentyzować szczegóły implementacyjne klas, co pozytywnie wpływa na końcową jakość naszego kodu. W językach niższego poziomu takich jak C#, Java etc. nie wyobrażam sobie większej aplikacji bez użycia interfejsów.

Przykładowy interfejs w TypeScript-cie wygląda następująco:

export interface ICar {
    drive (miles: number): boolean;
}

Widać tu liczne nawiązania do składni ES6.

Oczywiście w JavaScript-cie nie istnieje taki byt jak interfejs więc po skompilowaniu powyższego kodu otrzymamy całe nic... zero, nie powstanie nawet jedna linijka kodu.

Pewnie zastanawiacie się w jakim celu dokładać sobie pracy, jeżeli nie będzie żadnego odzwierciedlenia w kodzie wynikowym? Sprawdzanie poprawności implementacji interfejsów, tak samo jak sprawdzanie poprawności typów przekazywanych jako parametr etc. przebiega na poziomie kompilatora TypeScript-a. Dzięki temu o wielu błędach możemy dowiedzieć się od kompilatora, a nie od klienta :)

Sama składnia do interpretacji interfejsu w klasie nie musi być trudna. Poniżej przedstawiam najprostszy przykład na jaki wpadłem.

export class Car implements ICar {
    drive(miles: number): boolean {
        return true;
    }
}

Rysa na szkle

Wszystko super, prosto, przyjemni, ale... No właśnie to 'ale' to qlue całego artykułu. Ostatnio pracowałem nad projektem, w którym Frontend jest pisany w czystym TypeScriptcie. Nie ma tam żadnego React-a, Vue, Angular-a, jest tylko sam kod. No i tak pracując sobie jak nigdy nic zauważyłem małą wadę interfejsów TypeScript - a. Stworzenie takowego zawierającego deklarację konstruktora jest dość... hmmm... skomplikowane.

TypeScript nawet zawiera specjalną składnię, aby móc w interfejsie zdefiniować konstruktor:

export interface ICar {
    new(engine: Engine): ICar;
}

Niestety, próbując zaimplementować ten interfejs otrzymamy błąd :(

Możecie to zobaczyć w specjalnie przygotowanym przez mnie linku, który poprowadzi was do edytora na stronie TypeScript-a.

Przejdźmy teraz do tego, co misie lubią najbardziej i nie, nie do kakałka, a obejścia problemu jak największym łukiem.

Factory na pomoc

Moim sposobem na posiadanie interfejsu z konstruktorem jest wykorzystanie fabryki. W najprostszej postaci wystarczy nam pojedyncza funkcja, która posłuży do wytwarzania obiektów klasy Car. Jednocześnie, musimy wynieść deklaracje konstruktora do zewnętrznego interfejsu.

export interface CarConstructor {
    new(engine: Engine): Car;
}

export function CarFactory(ctor: CarConstructor, engine: Engine): Car {
    return new ctor(engine);
}

Coś takiego jest nawet opisane w dokumentacji TypeScript-a. Teraz, jeżeli chcemy utworzyć obiekt klasy Car, wystarczy:

let engine = new Engine();
let car = CarFactory(Car, engine);

Dlaczego?

Teraz pytanie, dlaczego musimy robić takie sztuczki? Z jednej strony chowamy wewnętrzne szczegóły instancjonowania klasy Car, aczkolwiek odczuwamy (tak, wszyscy odczuwamy) lekki zawód z braku możliwości implementacji interfejsu z konstruktorem.

Kompilator TypeScript sprawdza tylko instancję klasy, nie część statyczną. A co się pod nią kryje? Między innymi konstruktor. Kompilator go nie sprawdzi, więc nie będzie wiedział, czy jego implementacja jest poprawna :(

Podsumowanie

Dziś było trochę nietypowo, jednak mam nadzieję, że nie przeszkadzają wam pewne urozmaicenia raz na jakiś czas.

Oczywiście mam nadzieje, że artykuł się przyda :)

Do Następnego!

Cześć :)

By Bd90 | 16-08-2018 | Frontend, TypeScript