Interfejsy pozwalają podnieść poziom abstrakcji i uzyskać luźniejsze powiązania pomiędzy klasami w naszych aplikacjach. Pozwala to projektować zależności nie ograniczając się do konkretnego typu – wystarczy, aby dany typ spełniał zadany interfejs.
Co to znaczy – „spełniał interfejs”?
Interfejsy są kontraktem – to znaczy, jeżeli dana klasa implementuje konkretny interfejs to zobowiązuje się ona do spełnienia jego wszystkich wymogów. Jakie są wymogi? Najczęściej ograniczają się one do utworzenia metod o konkretnej sygnaturze. Język TypeScript pozwala nam dodatkowo wymuszać m.in. składowe klas.
Składnia
Jako przykład stworzymy interfejs, który wymusza na wszystkich klasach, które go implementują:
- metodę o nazwie
order()
, - składową o nazwie
price
.
Przykład interfejsu:
interface Orderable {
price: number;
order(): void;
}
Przykład klasy implementującej podany interfejs:
class Pizza implements Orderable {
public price: number;
public order(): void {}
}
Możemy zauważyć, że aby zadeklarować nowy interfejs korzystamy ze słowa kluczowego interface
, natomiast aby wymusić na klasie jego implementację – słowo implements
. To szczególnie powinno cieszyć programistów języka Java, którzy zdążyli się do tych słów przyzwyczaić.
Co w przypadku, kiedy klasa powinna implementować wiele interfejsów?
Na szczęście i w tym wypadku, nic nie stoi nam na przeszkodzie. Kolejne interfejsy wystarczy oddzielać przecinkami w następujący sposób:
class Pizza implements Orderable, Edible
Dziedziczenie interfejsów
Interfejsy, podobnie jak klasy – możemy dziedziczyć. Wykorzystujemy do tego słowo kluczowe extends
:
interface Food extends Orderable, Edible
Interfejsy a funkcje anonimowe
Dzięki elastyczności języka JavaScript, tworzenie funkcji anonimowych jest bardzo proste. Przykład:
let fn = (x: number, y: number): number => x / y;
W jaki sposób, przy pomocy interfejsu, wymusić określony typ zmiennej przechowującej tego typu funkcję? Rozwiązanie:
interface Function {
(x: number, y: number): number;
}
let fn: Function;
fn = (x: number, y: number): number => x / y;
Składowe opcjonalne
Jeżeli zachodzi taka potrzeba, niektóre zmienne możemy oznaczyć jako opcjonalne. Służy do tego znak ?
:
interface Person {
name: string;
age?: number;
}
function addPerson(person: Person) {
// do some stuff here
}
let person: Person;
person = {
name: "John"
};
addPerson(person);
Powyższy kod skompiluje się, pomimo że obiekt person
nie posiada składowej age
.
Podsumowanie
Celem dzisiejszego wpisu było zaprezentowanie składni i możliwości interfejsów w języku TypeScript. Serdecznie zachęcam do przeczytania również poprzedniego wpisu dotyczącego klas – tutaj.