Paweł Łukasiewicz
2024-12-02
Paweł Łukasiewicz
2024-12-02
Udostępnij Udostępnij Kontakt
Wprowadzenie

W programowaniu obiektowym, jednym z głównych sposobów organizacji kodu jest strukturalne definiowanie obiektów i ich właściwości. W TypeScript ten proces odbywa się za pomocą interfejsów. Interfejsy pozwalają na precyzyjne definiowanie kształtu obiektów i zapewniają, że wszystkie obiekty używane w kodzie spełniają określone wymagania. W tej części dowiemy się, jak definiować interfejsy oraz jak z nich korzystać.

Czym jest interfejs?

Interfejs to swego rodzaju kontrakt, który mówi, jakie właściwości i metody powinien mieć dany obiekt. Dzięki interfejsom możemy definiować strukturę danych bez bezpośredniej implementacji logiki. Interfejsy są szczególnie przydatne, gdy chcemy zdefiniować złożone obiekty lub upewnić się, że różne części kodu działają na spójnych danych.

Definiowanie prostego interfejsu

Aby stworzyć interfejs w TypeScript, używamy słowa kluczowego interface, a następnie określamy właściwości i ich typy. Oto prosty przykład:

interface Person {
    name: string;
    age: number;
}

W powyższym przykładzie zdefiniowaliśmy interfejs Person, który określa, że każdy obiekt tego typu musi mieć dwie właściwości:
  • name typu string.
  • age typu number.

Używanie interfejsu

Po zdefiniowaniu interfejsu możemy go użyć do typowania zmiennych, funkcji lub klas. Dzięki temu TypeScript będzie pilnował, aby każda instancja obiektu była zgodna z interfejsem.

Przykład:

let user: Person = {
    name: "Jan Kowalski",
    age: 30
};

Jeśli spróbujemy przypisać obiekt, który nie spełnia wymagań interfejsu, TypeScript zgłosi błąd:

let invalidUser: Person = {
    name: "Anna",
    // Brakuje właściwości 'age'
}; // Błąd! Brak właściwości 'age'

Właściwości opcjonalne

Czasami nie wszystkie właściwości obiektu muszą być obowiązkowe. W takim przypadku możemy oznaczyć właściwości jako opcjonalne za pomocą ?.

Przykład:

interface Car {
    model: string;
    year?: number; // Opcjonalna właściwość
}

let myCar: Car = {
    model: "Toyota"
};

let anotherCar: Car = {
    model: "Honda",
    year: 2015
};
Właściwość year w interfejsie Car jest opcjonalna, więc obiekt może ją mieć, ale nie musi.

Funkcje w interfejsach

Interfejsy mogą również definiować metody, czyli funkcje, które będą częścią obiektu. Możemy określić typ parametrów oraz typ zwracany funkcji.

Przykład:

interface Person {
    name: string;
    age: number;
    greet(): string; // Definicja funkcji w interfejsie
}

let user: Person = {
    name: "Jan Kowalski",
    age: 30,
    greet(): string {
        return `Cześć, jestem ${this.name}`;
    }
};

console.log(user.greet()); // Cześć, jestem Jan Kowalski
Funkcja greet() została zdefiniowana w interfejsie Person i każda implementacja tego interfejsu musi dostarczyć tę funkcję.

Rozszerzanie interfejsów

Interfejsy mogą być rozszerzane, co pozwala na ich łączenie i tworzenie bardziej złożonych struktur. Jest to szczególnie przydatne, gdy chcemy budować bardziej zaawansowane struktury danych na bazie prostszych elementów.

Przykład:

interface Employee {
    employeeId: number;
}

interface Manager extends Person, Employee {
    department: string;
}

let manager: Manager = {
    name: "Anna Nowak",
    age: 40,
    employeeId: 12345,
    department: "IT",
    greet(): string {
        return `Cześć, jestem ${this.name}, menadżer działu ${this.department}`;
    }
};
W tym przypadku interfejs Manager rozszerza zarówno interfejs Person, jak i Employee, co oznacza, że obiekt typu Manager musi spełniać wymagania obu interfejsów.

Typy indeksowane

Interfejsy mogą również definiować typy indeksowane, co oznacza, że możemy dynamicznie definiować klucze obiektów.

Przykład:

interface StringDictionary {
    [key: string]: string;
}

let myDictionary: StringDictionary = {
    firstName: "Jan",
    lastName: "Kowalski"
};
W tym przykładzie interfejs StringDictionary pozwala na tworzenie obiektów, gdzie klucze są typu string, a wartości również są typu string.

Interfejsy w funkcjach

Interfejsy mogą również być używane do definiowania typów parametrów funkcji, co sprawia, że funkcje mogą przyjmować tylko obiekty zgodne z danym interfejsem.

Przykład:

function printPersonDetails(person: Person): void {
    console.log(`Imię: ${person.name}, Wiek: ${person.age}`);
}

let user: Person = { name: "Jan", age: 30 };
printPersonDetails(user); // Imię: Jan, Wiek: 30
Funkcja printPersonDetails akceptuje tylko obiekty typu Person, co zapewnia, że zawsze otrzyma obiekt zgodny z interfejsem.

Podsumowanie

Interfejsy w TypeScript są niezwykle potężnym narzędziem, które pozwala na precyzyjne definiowanie kształtu obiektów, co sprawia, że kod staje się bardziej spójny i łatwiejszy w utrzymaniu. Kluczowe elementy interfejsów to:

  • Definiowanie właściwości i metod.
  • Właściwości opcjonalne.
  • Rozszerzanie interfejsów.
  • Używanie interfejsów w funkcjach i do typowania zmiennych.