TypeScript stał się standardem branżowym w tworzeniu aplikacji webowych na dużą skalę. Podczas gdy większość programistów zna podstawy interfejsów i typów, prawdziwa siła tkwi w zaawansowanym systemie typów. Oto 5 wzorców, które wyróżnią Cię jako starszego inżyniera.
1. Generic Constraints
Generics są potężne, ale czasami musisz ograniczyć to, co może zostać przekazane. extends jest tutaj Twoim przyjacielem.
interface HasId { id: string; } function getById<T extends HasId>(list: T[], id: string): T | undefined { return list.find((item) => item.id === id); }
Zapewniając, że T rozszerza HasId, gwarantujemy, że dostęp do .id wewnątrz funkcji jest bezpieczny.
2. Conditional Types
Typy warunkowe pozwalają tworzyć niejednolite mapowania typów. Składnia jest podobna do operatora trójargumentowego w JavaScript.
type IsString<T> = T extends string ? true : false; type A = IsString<string>; // true type B = IsString<number>; // false
Praktycznym zastosowaniem jest filtrowanie typów z unii:
type Diff<T, U> = T extends U ? never : T; type NonNullable<T> = Diff<T, null | undefined>;
3. Mapped Types
Mapped types pozwalają tworzyć nowe typy na podstawie istniejących poprzez transformację właściwości.
type ReadOnly<T> = { readonly [P in keyof T]: T[P]; }; interface User { name: string; age: number; } type ReadOnlyUser = ReadOnly<User>;
Możesz nawet dodawać lub usuwać modyfikatory:
type Mutable<T> = { -readonly [P in keyof T]: T[P]; };
4. Template Literal Types
Wprowadzone w TypeScript 4.1, pozwalają manipulować typami stringów bezpośrednio.
type World = 'world'; type Greeting = `hello ${World}`; // "hello world" type Color = 'red' | 'blue'; type Quantity = 'light' | 'dark'; type Palette = `${Quantity}-${Color}`; // "light-red" | "light-blue" | "dark-red" | "dark-blue"
Jest to niezwykle przydatne do typowania stringów, które podążają za określonym wzorcem, jak klasy CSS czy nazwy zdarzeń.
5. Słowo kluczowe infer
Słowo kluczowe infer w typach warunkowych pozwala wyodrębniać typy z innych typów.
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any; function check(): boolean { return true; } type CheckReturn = ReturnType<typeof check>; // boolean
Tutaj prosimy TypeScript o "wydedukowanie" typu zwracanego R funkcji i zwrócenie go.
Podsumowanie
Opanowanie tych wzorców pozwala pisać biblioteki i narzędzia, które są solidne i zapewniają doskonałe doświadczenie programisty (DX). Celem zaawansowanego TypeScript nie jest złożoność dla samej złożoności, ale bezpieczeństwo i ekspresyjność.