Przejdź do głównej treści
eLearner.app
Moduł 3 · Lekcja 2 z 26/14 w kursie~15 min
Lekcje modułu (2/2)

Podstawy typów generycznych

Typy generyczne / Generics (programowanie uogólnione) są jedną z najpotężniejszych funkcji języka TypeScript. Pozwalają one na tworzenie elastycznych, reużywalnych komponentów, funkcji i interfejsów, które mogą współpracować z różnymi typami danych, zachowując jednocześnie maksymalne bezpieczeństwo typów (type safety) i unikając stosowania typu any.


Funkcje Generyczne

Pomyśl o typie generycznym jak o zmiennej przechowującej typ. Rozważmy funkcję, która zwraca przekazaną do niej wartość:

TS
function identity<T>(arg: T): T {
  return arg;
}

Litera T (skrót od Type) to symbol zastępczy (placeholder) dla typu. Kiedy wywołujemy identity, TypeScript przechwytuje typ przekazanego argumentu i automatycznie przypisuje go do T:

TS
const str = identity<string>('Hello'); // T è string
const num = identity(42); // T è number (inferito automaticamente!)

Interfejsy i Aliasy Generyczne

Typy generyczne można stosować także do interfejsów i aliasów typów, co pozwala na definiowanie generycznych kontenerów lub struktur danych:

TS
interface Box<T> {
  content: T;
}

const stringBox: Box<string> = { content: 'TypeScript' };
const numberBox: Box<number> = { content: 42 };

Wielokrotne Parametry Typów

Możemy definiować funkcje lub typy korzystające z wielu zmiennych typów (np. T i U):

TS
function mergeObjects<T, U>(obj1: T, obj2: U): T & U {
  return { ...obj1, ...obj2 };
}

Ograniczenia Typów Generycznych (Generic Constraints)

Czasami chcemy ograniczyć typy akceptowane przez parametr generyczny. Możemy użyć słowa kluczowego extends, aby dodać ograniczenie (constraint).

Na przykład, jeśli chcemy, aby typ generyczny zawsze posiadał właściwość length (tak jak napisy czy tablice):

TS
interface HasLength {
  length: number;
}

function logLength<T extends HasLength>(arg: T): void {
  console.log(arg.length); // Sicuro! Sappiamo che arg ha 'length'
}

logLength('Hello'); // Valido
logLength([1, 2, 3]); // Valido
// logLength(42); // Errore: number non ha la proprietà length!

Spróbuj sam

Ćwiczenie 1: Generyczna funkcja identity

Ćwiczenie#ts.m3.l2.e1
Próby: 0Ładowanie...

Uzupełnij funkcję generyczną identity tak, aby przyjmowała parametr value o typie generycznym T i zwracała go bez żadnych modyfikacji.

Ładowanie edytora...
Pokaż wskazówkę

Typ zwracany przez funkcję musi być tym samym typem generycznym T co typ parametru.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie 2: Generyczny Box

Ćwiczenie#ts.m3.l2.e2
Próby: 0Ładowanie...

Zdefiniuj interfejs generyczny o nazwie Box, który przyjmuje parametr typu T i posiada jedną właściwość o nazwie content typu T.

Ładowanie edytora...
Pokaż wskazówkę

Użyj składni interface Box<T> { content: T; }.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie 3: Ograniczenie Typu Generycznego (Constraint)

Ćwiczenie#ts.m3.l2.e3
Próby: 0Ładowanie...

Napisz funkcję generyczną o nazwie getLength, która przyjmuje parametr arg. arg must być ograniczony do interfejsu posiadającego właściwość length (liczba). Funkcja powinna zwracać arg.length.

Ładowanie edytora...
Pokaż wskazówkę

Zadeklaruj funkcję jako getLength<T extends HasLength>(arg: T): number i zwróć arg.length.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie 4: Generyczna para Pair

Ćwiczenie#ts.m3.l2.e4
Próby: 0Ładowanie...

Zdefiniuj interfejs generyczny Pair<T, U> z dwiema właściwościami: first typu T oraz second typu U. Następnie utwórz funkcję generyczną makePair, która przyjmuje parametry first (typu T) oraz second (typu U) i zwraca obiekt implementujący interfejs Pair<T, U>.

Ładowanie edytora...
Pokaż wskazówkę

Użyj składni z dwoma parametrami generycznymi Pair<T, U> i odpowiednio zdefiniuj makePair.

Rozwiązanie dostępne po 3 próbach