Autor podstrony: Krzysztof Zajączkowski

Stronę tą wyświetlono już: 3498 razy

Interfejsy w Angularze umożliwiają wprowadzenie kontroli typów oraz ułatwiają pracę z danymi zapisanymi w formacie JSON, który jak wiadomo jest formatem tekstowym. Każdy interfejs może zawierać jedynie pola wraz z opcjonalnym opisem ich typów. Oto przykładowy prosty interfejs:

export interface Engine { power: number; maxNPM: number; }

Słowo kluczowe export oznacza, że taki interfejs będzie można eksportować w innym pliku projektu.

Jeżeli zachodzi konieczność uniemożliwienia raz ustawionej wartości pola interfejsu można do tego celu wykorzystać słowo kluczowe readonly w następujący sposób:

export interface Engine { readonly power: number; readonly maxNPM: number; }

Po utworzeniu instancji tak zmodyfikowanego interfejsu:

let engine: Engine = { power: 150, maxNPM: 6000 };

Nadpisanie pól typu readonly nie będzie możliwe.

Istnieje też możliwość utworzenia interfejsu, który będzie przyjmował dowolną liczbę pól np. dowolnego typu. Oto przykład takiego interfejsu:

export interface MyHtml{ name: string; [style: string]: any; }

W powyższym przykładzie interfejs ma jedno obowiązkowe pole name oraz może przyjmować dowolną liczbę pól typu any. Oto przykład:

let myHtml: MyHtml = { name: 'b', color: 'white' }; console.log(myHtml);

Powyższy kod wyświetli w konsoli następujące dane:

{ name: 'b', color: 'white' }

Jeżeli dany interfejs posiada sporą liczbę pól do wypełnienia, ale niektóre z nich są opcjonalne, to można je takimi uczynić przy wykorzystaniu znaku ? w następujący sposób:

export interface CarInterface { brand: string; maxSpeed: number; acceleriation: number; weight: number; airConditioning?: any; // opcjonalny argument, który może ale nie musi być ustawiony }

Można też utworzyć obiekt typu Partial, dzięki czemu wszystkie argumenty będą opcjonalne. Oto przykład:

// standardowe utworzenie obiektu typu CarInterface z pominięciem opcjonalnego argumentu let car1: CarInterface = { brand: 'Ford Mustang', maxSpeed: 250, acceleriation: 5.7, weight: 1000 }; // utworzenie obiektu typu Partial<CarInterface> z pominięciem kilku nie opcjonalnych w interfejsie CarInterface pól let car2: Partial<CarInterface> = {brand: 'Ford Mustang' };

Problem może się pojawić, gdy jakaś metoda czy funkcja będzie wymagała pełnego obiektu a będąc w posiadaniu okrojonego obiektu nie jest możliwe w bezpośredni sposób przekazanie takiego niepełnego obiektu. W takim przypadku można pokusić się o wykorzystanie słowa kluczowego as w następujący sposób:

let car3: CarInterface = { brand: 'Ford Mustang', maxSpeed: 250 } as CarInterface console.log(car3)

Wynik wyświetlony w konsoli:

Object { brand: "Ford Mustang", maxSpeed: 250, acceleriation: 5.7, weight: 1000 }

Interfejsy można w łatwy sposób rozszerzać poprzez wykorzystanie słowa kluczowego extends w następujący sposób:

export interface Engine { power: number; maxNPM: number; } export interface CarInterface extends Engine { brand: string; maxSpeed: number; acceleriation: number; weight: number; turnOnEngine?(): void; }

Teraz interfejs CarInterface został rozszerzony o pola, które posiada interfejs Engine. Zmiana danych zapisanych w formacie JSON na zmienną typu Engine sprowadza się jedynie do sparsowania tegoż JSON-a:

let engineJson: string = '{ "power": 500, "maxNPM": 7000 }'; let engine: Engine = JSON.parse(engineJson);

Oczywiście dane zawarte w JSON-ie powinny odpowiadać tym z interfejsu. Jeżeli tak nie będzie to niestety ale TypeScript doda pola, które według interfejsu nie powinny istnieć w obiekcie.

W tym przypadku zmienna engine jest zmienną typu Engine, dzięki czemu sam Visual Studio Code będzie podpowiadał pola, jakie ten obiekt posiada.

Interfejsy mogą również zawierać deklaracje funkcji np. w taki oto sposób:

export interface Engine { power: number; maxNPM: number; } export interface CarInterface extends Engine { brand: string; maxSpeed: number; acceleriation: number; weight: number; turnOnEngine(): void; }

Interfejsy jednak nie umożliwiają tworzenie pełnych deklaracji funkcji ani też nie pozwalają na inicjalizację pól interfejsu danymi. Z kolei inicjalizacja zmiennych za pomocą JSON-a ogranicza się tylko do wartości liczbowych, tekstowych, tablicowych lub obiektów, których pola składają się z takich właśnie elementów. Dzieje się tak, ponieważ JSON jest przystosowany jedynie do przechowywania danych nie zaś deklaracji funkcji. W celu ustawienia zmiennej typu funkcyjnego konieczne jest ręczne przypisanie tejże funkcji. Oto przykład:

let carJson: string = '{ "power": 500, "maxNPM": 7000, "brand": "Ford Mustang", "maxSpeed": 250, "acceleriation": 7.5, "weight": 1000 }'; let car: CarInterface = JSON.parse(carJson); // Inicjalizacja pola funkcji zmiennej car car.turnOnEngine = () => { console.log('Engiine is turn on') }; // Uruchomienie funkcji car.turnOnEngine(); // Wyświetlenie obiektu funkcji console.log('Car', car);

Interfejsy można wykorzystywać do wymuszania obsługi określonych metod przez klasy. Taki mechanizm wykorzystują Angular-owe haki. Oto prosty przykład użycia takiego mechanizmu:

export interface CarI { turnOnEngine(): void; } export class Car implements CarI { turnOnEngine(): void { console.log('Engine is running now'); } }

W momencie, gdy klasa implementuje interfejs, wszystkie pola oraz deklaracje metod muszą być utworzone również i w klasie. Wymusza to również obsługę wszystkich funkcji w deklaracji klasy.

Co ciekawe można również rozszerzać interfejs za pomocą klasy. W takim przypadku interfejs automatycznie zyskuje wszystkie pola i metody klasy bazowej. Oto przykład:

export class Square { a: number; constructor(a: number) { this.a = a; } get p(): number { return this.a * Math.sqrt(2); } set p(pValue: number) { this.a = pValue / Math.sqrt(2); } get area(): number { return this.a * this.a; } set area(areaValue: number) { this.a = Math.sqrt(areaValue); } print() { console.log('Cechy obiektu prostokąta:'); console.log('Długość boku a: ' + this.a); console.log('Długość przekątnej b: ' + this.p); console.log('Pole powierzchni Ppow: ' + this.area); } } export interface SquareInterface extends Square { }

Interfejs SquareInterface został rozszerzony o pola klasy Square przez co automatycznie dysponuje on wszystkimi polami, jakie klasa rozszeżająca dysponowała.

Layout wykonany przez autora strony, wszelkie prawa zastrzeżone. Jakiekolwiek użycie części lub całości grafik znajdujących się na tej stronie bez pisemnej zgody jej autora surowo zabronione.