Generyki w TypeScript
Ten artykuł wyjaśnia generyki w TypeScript.
YouTube Video
Generyki w TypeScript
Generyki w TypeScript to funkcja, która pozwala definiować wielokrotnego użytku oraz typowo-bezpieczne funkcje, klasy i interfejsy poprzez parametryzację typów. Używanie generyków pozwala pisać kod, który nie zależy od konkretnych typów, umożliwiając wykonywanie tych samych operacji na różnych typach.
Podstawy generyków
Generyki działają jak szablony, które akceptują typy jako argumenty, pozwalając funkcjom i klasom obsługiwać różne typy.
Funkcje generyczne
Poniżej znajduje się przykład funkcji, której typy argumentów zostały określone za pomocą generyków.
1function identity<T>(value: T): T {
2 return value;
3}
4
5console.log(identity<number>(42)); // 42
6console.log(identity<string>("Hello")); // Hello
T
to argument typu generycznego, który reprezentuje typy argumentów funkcji oraz jej wartość zwracaną. Rzeczywisty typ jest określany w momencie wywołania funkcji.- Poprzez jawne określenie
<number>
lub<string>
definiujesz typ.
Generyczne typy działają bez jawnego określenia, ponieważ TypeScript przeprowadza inferencję typów.
1function identity<T>(value: T): T {
2 return value;
3}
4
5console.log(identity(42)); // 42
6console.log(identity("Hello")); // Hello
Ograniczenia generyków
Poprzez nałożenie ograniczeń na generyki, możesz ograniczyć je do akceptowania tylko określonych typów.
1function loggingIdentity<T extends { length: number }>(arg: T): T {
2 console.log(arg.length);
3 return arg;
4}
5
6loggingIdentity("Hello"); // 5
7loggingIdentity([1, 2, 3]); // 3
8
9// loggingIdentity(42); // Error: number does not have a length property.
- Określenie
T extends { length: number }
wskazuje, żeT
musi być typem posiadającym właściwośćlength
. W związku z tym, typy bez właściwościlength
nie będą akceptowane.
Klasy generyczne
Klasy również mogą być definiowane przy użyciu generyków. Klasy generyczne oferują elastyczne typy dla właściwości i metod.
1class Box<T> {
2 private _value: T;
3
4 constructor(value: T) {
5 this._value = value;
6 }
7
8 public getValue(): T {
9 return this._value;
10 }
11
12 public setValue(value: T): void {
13 this._value = value;
14 }
15}
16
17const numberBox = new Box<number>(100);
18console.log(numberBox.getValue()); // 100
19
20const stringBox = new Box<string>("Hello");
21console.log(stringBox.getValue()); // Hello
Box<T>
deklaruje typT
używany w ramach klasy jako generyczny. Dzięki temu ta sama klasa może być wielokrotnie używana dla różnych typów.
Interfejsy generyczne
Generyki mogą być również używane z interfejsami.
1interface Pair<T, U> {
2 first: T;
3 second: U;
4}
5
6const numberStringPair: Pair<number, string> = { first: 1, second: "One" };
7console.log(numberStringPair); // { first: 1, second: 'One' }
8
9const booleanArrayPair: Pair<boolean, number[]> = { first: true, second: [1, 2, 3] };
10console.log(booleanArrayPair); // { first: true, second: [ 1, 2, 3 ] }
- Określając dwa generyczne typy za pomocą
Pair<T, U>
, możesz zdefiniować obiekt zawierający kombinację różnych typów.
Domyślne argumenty typów
Możliwe jest również określenie domyślnego typu dla argumentów typów generycznych.
1function createArray<T = string>(length: number, value: T): T[] {
2 return Array(length).fill(value);
3}
4
5console.log(createArray(3, "a")); // ['a', 'a', 'a']
6console.log(createArray(3, 100)); // [100, 100, 100]
- Ustawiamy domyślny argument typu na
string
za pomocą<T = string>
. Jeśli żaden typ nie zostanie jawnie określony,T
będzie typustring
.
Generyczne aliasy typów
Generyki mogą być również używane jako aliasy typów (type
).
1type Result<T> = {
2 success: boolean;
3 data: T;
4};
5
6const successResult: Result<number> = { success: true, data: 42 };
7const errorResult: Result<string> = { success: false, data: "Error occurred" };
8
9console.log(successResult); // { success: true, data: 42 }
10console.log(errorResult); // { success: false, data: 'Error occurred' }
Result<T>
reprezentuje obiekt wyniku zawierający dane typuT
. W ten sposób możesz tworzyć elastyczne aliasy typów za pomocą generyków.
Wiele typów generycznych
Korzystając z wielu typów generycznych, możesz definiować jeszcze bardziej wszechstronne funkcje i klasy.
1function merge<T, U>(obj1: T, obj2: U): T & U {
2 return { ...obj1, ...obj2 };
3}
4
5const person = { name: "Alice" };
6const job = { title: "Engineer" };
7
8const merged = merge(person, job);
9console.log(merged); // { name: 'Alice', title: 'Engineer' }
- Funkcja
merge
przyjmuje dwa różne typyT
iU
, a następnie łączy je w nowy obiekt.
Podsumowanie
- Generyki umożliwiają tworzenie wielokrotnego użytku i typ-bezpiecznego kodu, traktując typy jako parametry.
- Korzystając z generyków w funkcjach, klasach i interfejsach, możesz tworzyć elastyczną logikę obsługującą różne typy.
- Dodając ograniczenia do argumentów typów lub ustawiając domyślne argumenty typów, możesz kontrolować zakres generyków.
Za pomocą generyków możesz pisać niezależny od typu, uniwersalny kod, w pełni wykorzystując potężny system typów TypeScript.
Możesz śledzić ten artykuł, korzystając z Visual Studio Code na naszym kanale YouTube. Proszę również sprawdzić nasz kanał YouTube.