TypeScript, będący rozszerzeniem JavaScript, wprowadza wsparcie dla pełnej programistycznej obiektowości, dzięki której możemy korzystać z takich konstrukcji jak klasy, dziedziczenie czy enkapsulacja. Te cechy sprawiają, że tworzenie złożonych aplikacji staje się prostsze i bardziej uporządkowane. W tej części przyjrzymy się, jak tworzyć klasy i instancje w TypeScript oraz jak działa dziedziczenie.
Czym są klasy?
Klasy w TypeScript są szablonami dla obiektów, które definiują ich właściwości oraz zachowania (metody). Z klasy możemy tworzyć obiekty, które nazywamy instancjami. Klasy mogą dziedziczyć po innych klasach, co umożliwia wielokrotne użycie kodu i organizację bardziej złożonych struktur.
Tworzenie prostej klasy
Tworzenie klasy w TypeScript jest bardzo podobne do innych obiektowych języków programowania. Używamy słowa kluczowego class, aby zdefiniować nową klasę, a następnie w jej wnętrzu definiujemy pola (czyli właściwości) oraz metody.
Przykład:
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): string {
return `Cześć, jestem ${this.name} i mam ${this.age} lat.`;
}
}
W tym przykładzie klasa Person ma dwie właściwości:
name typu string.
age typu number.
Oraz metodę greet(), która zwraca wiadomość zawierającą informacje o osobie.
Tworzenie instancji klasy
Aby stworzyć instancję klasy, używamy słowa kluczowego new, które tworzy nowy obiekt na podstawie klasy. Następnie możemy korzystać z właściwości oraz metod tego obiektu.
Przykład:
let person1 = new Person("Jan Kowalski", 30);
console.log(person1.greet()); // Cześć, jestem Jan Kowalski i mam 30 lat.
W ten sposób stworzyliśmy nowy obiekt person1 będący instancją klasy Person i wywołaliśmy jego metodę greet().
Modyfikatory dostępu: public, private, protected
W TypeScript możemy określić widoczność (czyli dostępność) właściwości oraz metod za pomocą modyfikatorów dostępu. Dzięki temu możemy kontrolować, które elementy klasy są dostępne na zewnątrz, a które są ukryte i dostępne tylko wewnątrz klasy:
public – domyślny modyfikator. Oznacza, że właściwość lub metoda jest dostępna na zewnątrz klasy.
private – oznacza, że właściwość lub metoda jest dostępna tylko wewnątrz klasy i nie można jej używać na zewnątrz.
protected – oznacza, że właściwość lub metoda jest dostępna tylko w klasie oraz w klasach dziedziczących.
Przykład:
class Person {
public name: string;
private age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
public greet(): string {
return `Cześć, jestem ${this.name}.`;
}
private getAge(): number {
return this.age;
}
}
let person = new Person("Anna", 25);
console.log(person.greet()); // Cześć, jestem Anna
// console.log(person.age); // Błąd! Właściwość 'age' jest prywatna.
W powyższym przykładzie name jest publiczne, więc możemy z niego korzystać na zewnątrz, ale age jest prywatne, więc nie możemy bezpośrednio do niej odwołać się poza klasą.
Dziedziczenie klas
Jedną z głównych zasad programowania obiektowego jest dziedziczenie. W TypeScript klasa może dziedziczyć właściwości i metody innej klasy za pomocą słowa kluczowego extends. Dzięki temu możemy tworzyć bardziej wyspecjalizowane klasy, które korzystają z cech bazowej klasy.
Przykład:
class Employee extends Person {
employeeId: number;
constructor(name: string, age: number, employeeId: number) {
super(name, age); // Wywołanie konstruktora klasy bazowej
this.employeeId = employeeId;
}
public showId(): string {
return `Pracownik ${this.name} ma ID: ${this.employeeId}`;
}
}
let employee1 = new Employee("Tomasz", 28, 12345);
console.log(employee1.greet()); // Cześć, jestem Tomasz
console.log(employee1.showId()); // Pracownik Tomasz ma ID: 12345
Klasa Employee dziedziczy po klasie Person, co oznacza, że posiada właściwości i metody klasy Person, a dodatkowo ma swoją własną właściwość employeeId oraz metodę showId().
Klasy abstrakcyjne
Klasy abstrakcyjne są specjalnym rodzajem klas, które nie mogą być bezpośrednio instancjonowane. Służą one jako szablony do tworzenia innych klas i mogą zawierać zarówno zaimplementowane metody, jak i metody abstrakcyjne, które muszą zostać zdefiniowane w klasach dziedziczących.
Przykład:
abstract class Animal {
abstract makeSound(): void; // Metoda abstrakcyjna
move(): void {
console.log("Zwierzę się porusza.");
}
}
class Dog extends Animal {
makeSound(): void {
console.log("Hau hau!");
}
}
let dog = new Dog();
dog.makeSound(); // Hau hau!
dog.move(); // Zwierzę się porusza.
Klasa Animal jest abstrakcyjna, więc nie możemy utworzyć jej instancji, ale możemy stworzyć instancję klasy Dog, która dziedziczy po Animal i implementuje metodę makeSound().
Polimorfizm
Polimorfizm to cecha, która pozwala na traktowanie obiektów różnych klas tak, jakby były obiektami klasy bazowej. Oznacza to, że możemy używać tej samej metody na różnych obiektach, nawet jeśli każdy obiekt zachowuje się inaczej.
Przykład:
class Cat extends Animal {
makeSound(): void {
console.log("Miau miau!");
}
}
let animals: Animal[] = [new Dog(), new Cat()];
animals.forEach((animal) => {
animal.makeSound();
});
W tym przykładzie mamy tablicę animals, która zawiera obiekty zarówno klasy Dog, jak i Cat, ale możemy wywoływać na nich tę samą metodę makeSound(), mimo że jej implementacja jest różna.
Podsumowanie
Klasy w TypeScript są podstawowym elementem programowania obiektowego. Dzięki nim możemy organizować kod w sposób przejrzysty i skalowalny. Kluczowe cechy klas w TypeScript to:
Tworzenie instancji obiektów na podstawie klas.
Dziedziczenie właściwości i metod z innych klas.
Kontrola dostępu do właściwości za pomocą modyfikatorów public, private, protected.
Używanie klas abstrakcyjnych i polimorfizmu, co pozwala na bardziej złożone i elastyczne struktury kodu.