·
Pytania i odpowiedzi TypeScript
TypeScript — interview Q&A
45 Pytań
0 Znanych
Zestaw 45 pytań rekrutacyjnych z TypeScript pogrupowanych w 5 kategorii: podstawy i system typów, typy zaawansowane, klasy i OOP, generyki i utility types oraz praktyka i konfiguracja. Kliknij pytanie, by zobaczyć odpowiedź.
🔍
45 pytań
🔍
Brak wyników dla podanej frazy
1 Czym jest TypeScript i czym różni się od JavaScript?

TypeScript to silnie typowany nadzbiór języka JavaScript opracowany przez Microsoft. Kod TS jest transpilowany do JS przez kompilator tsc, więc działa w każdym środowisku obsługującym JavaScript.

Kluczowe różnice: statyczna analiza typów w czasie kompilacji (błędy widoczne w IDE przed uruchomieniem), interfejsy i typy, dekoratory, generyki oraz pełna obsługa nowoczesnego ES2015+ z downcasterem. W runtime TypeScript nie istnieje — pozostaje czysty JS.

Podstawy i typy
2 Jaka jest różnica między interface a type w TypeScript?

interface służy do opisywania kształtu obiektów i może być rozszerzana (extends) oraz scalana przez deklarację merging — czyli dwie deklaracje tego samego interfejsu automatycznie się łączą.

type (alias) jest bardziej ogólny: może reprezentować unię (A | B), krotkę, typ prymitywny lub mapped type. Nie obsługuje merging. W praktyce: używaj interface dla publicznego API bibliotek i klas, type dla złożonych typów, unii i aliasów.

interface User { name: string; age: number; } type ID = string | number; type Pair<T> = [T, T];
Podstawy i typy
4 Co to jest type narrowing i jak działa w TypeScript?

Type narrowing to mechanizm, w którym kompilator TypeScript zawęża typ zmiennej na podstawie warunków w kodzie. Po sprawdzeniu warunków TypeScript „wie", jaki konkretny typ jest w danej gałęzi.

Techniki zwężania: typeof (typeof x === 'string'), instanceof, in operator ('prop' in obj), type predicates (x is Type), discriminated union (wspólne pole literalne) oraz assertion functions.

function process(val: string | number) { if (typeof val === 'string') { return val.toUpperCase(); // tu val: string } return val.toFixed(2); // tu val: number }
Podstawy i typy
5 Czym jest readonly i const — jaka jest między nimi różnica?

const to deklaracja zmiennej w JavaScript — blokuje ponowne przypisanie referencji, ale nie chroni zawartości obiektu (właściwości wciąż można modyfikować).

readonly to modyfikator TypeScript dla właściwości obiektów, interfejsów i klas — blokuje modyfikację danego pola po inicjalizacji. Istnieje też utility type Readonly<T> zwracający wersję interfejsu ze wszystkimi polami readonly.

Podstawy i typy
6 Czym są typy literalne (literal types) i jak się je stosuje?

Typy literalne pozwalają zawęzić typ do konkretnych wartości zamiast ogólnych prymitywów. Mogą to być stringi, liczby lub wartości logiczne.

Najczęstsze zastosowanie to string literal unions zastępujące enumy oraz const assertion (as const), która zamraża strukturę obiektu do typów literalnych. Pozwala to TypeScriptowi wykrywać niedozwolone wartości w czasie kompilacji.

type Direction = 'north' | 'south' | 'east' | 'west'; type StatusCode = 200 | 201 | 400 | 404 | 500; function move(dir: Direction) { /* ... */ }
Podstawy i typy
7 Czym są enumeracje (enum) w TypeScript i kiedy unikać ich używania?

enum to specjalna struktura TypeScript generująca obiekt JS w runtime. Domyślnie numeryczny (0, 1, 2…), może być też const enum (wbudowany inline, bez obiektu w output) lub string enum.

Kiedy unikać: numeric enum umożliwia przypisanie dowolnej liczby (brak type safety), const enum jest problematyczny z izolowanymi modułami (Babel, esbuild). Alternatywa: as const + string literal union — czyste TS, zero narzutu w runtime, pełna type safety.

Podstawy i typy
8 Co to są type guards i jak tworzy się własne?

Type guard to wyrażenie, które w runtime sprawdza typ i jednocześnie informuje kompilator o zawężeniu. Wbudowane: typeof, instanceof, in.

Własny type guard to funkcja zwracająca type predicate: parameterName is Type. Jeśli funkcja zwróci true, TypeScript traktuje parametr jako dany typ w danej gałęzi. Używaj gdy dane zewnętrzne (API, JSON) muszą zostać zweryfikowane i bezpiecznie opatrzone typem.

function isUser(obj: unknown): obj is User { return typeof obj === 'object' && obj !== null && 'name' in obj && 'age' in obj; }
Podstawy i typy
9 Czym jest structural typing (duck typing) w TypeScript?

TypeScript stosuje structural typing — kompatybilność typów wynika z kształtu (struktury) obiektu, nie z nazwy klasy czy interfejsu. Jeśli obiekt ma wszystkie wymagane właściwości, jest kompatybilny z danym typem.

Różni się to od nominal typing (C#, Java), gdzie klasy muszą explicite deklarować implementację interfejsu. W TypeScript wystarczy, że obiekt pasuje strukturalnie — nazywane to bywa „duck typing": jeśli chodzi jak kaczka i kwacze jak kaczka, to jest kaczką.

Podstawy i typy
10 Czym są union types i intersection types? Podaj przykłady zastosowań.

Union type (A | B) oznacza, że wartość może być jednym z podanych typów. Wymaga type narrowing przed użyciem specyficznych metod. Typowe zastosowanie: parametry funkcji przyjmujące wiele typów, odpowiedzi API z różnymi kształtami.

Intersection type (A & B) łączy typy — wynikowy obiekt musi spełniać oba jednocześnie. Używany do kompozycji interfejsów (mixiny) i rozszerzania typów bez dziedziczenia.

type AdminUser = User & { permissions: string[] }; type Result = { data: string } | { error: Error };
Typy zaawansowane
11 Czym są discriminated unions i do czego służą?

Discriminated union (tagged union) to wzorzec, w którym każdy wariant unii posiada wspólne pole literalne (discriminant) umożliwiające jednoznaczne rozróżnienie. TypeScript automatycznie zawęża typ w każdej gałęzi switch/if.

Wzorzec jest szczególnie przydatny przy modelowaniu stanów aplikacji (loading / success / error), akcji Redux/Zustand oraz wyników operacji asynchronicznych. Razem z never umożliwia exhaustiveness checking — kompilator ostrzega, gdy nie obsłużono wszystkich wariantów.

type Shape = | { kind: 'circle'; radius: number } | { kind: 'rect'; width: number; height: number }; function area(s: Shape): number { switch (s.kind) { case 'circle': return Math.PI * s.radius ** 2; case 'rect': return s.width * s.height; } }
Typy zaawansowane
12 Czym są mapped types i jak działają?

Mapped types pozwalają tworzyć nowe typy poprzez iterację po kluczach innego typu za pomocą składni { [K in keyof T]: ... }. Na tym mechanizmie opierają się wbudowane utility types.

Można dodawać lub usuwać modyfikatory (+readonly, -readonly, +?, -?) oraz transformować typy właściwości. Przydatne do tworzenia wersji obiektów tylko do odczytu, opcjonalnych, nullowalnych itp.

type Optional<T> = { [K in keyof T]?: T[K] }; type Nullable<T> = { [K in keyof T]: T[K] | null };
Typy zaawansowane
13 Czym są conditional types i jak ich używać?

Conditional types działają jak operator trójargumentowy na poziomie typów: T extends U ? X : Y. Jeśli T jest przypisywalny do U, wynikiem jest X, w przeciwnym razie Y.

Kluczową funkcją jest distributivity — stosowane na unii działają na każdym wariancie osobno. Razem ze słowem kluczowym infer pozwalają wyciągać fragmenty typów (np. typ zwracany funkcji, parametry). Na tym opierają się ReturnType, Parameters, Awaited.

type IsArray<T> = T extends any[] ? true : false; type Unwrap<T> = T extends Promise<infer R> ? R : T;
Typy zaawansowane
14 Czym są template literal types i kiedy je stosować?

Template literal types (TS 4.1+) pozwalają budować typy stringowe z innych typów przy użyciu składni szablonów: `prefix_${T}`. Stosowane na unii stringów tworzą unię wszystkich kombinacji.

Zastosowania: generowanie kluczy zdarzeń (`on${EventName}`), walidacja formatów (np. `${number}px`), typowane ścieżki CSS, ścieżki obiektów JSON (dot notation). Razem z Capitalize, Uppercase i innymi intrinsic types tworzą zaawansowane API.

type EventName = 'click' | 'focus' | 'blur'; type Handler = `on${Capitalize<EventName>}`; // 'onClick' | 'onFocus' | 'onBlur'
Typy zaawansowane
15 Co to są dekoratory (decorators) w TypeScript?

Dekoratory to specjalne funkcje poprzedzone @ stosowane do klas, metod, właściwości lub parametrów. Umożliwiają modyfikację zachowania w stylu aspect-oriented programming.

Dekoratory klas są powszechne w Angular (@Component, @Injectable) i NestJS (@Controller, @Get). Od TypeScript 5.0 obowiązuje nowa specyfikacja TC39 Stage 3 — różni się od starszego API eksperymentalnego. Wymagają włączenia experimentalDecorators (legacy) lub są dostępne natywnie w TS 5+.

Typy zaawansowane
16 Czym jest keyof i typeof na poziomie typów?

keyof T zwraca unię wszystkich kluczy typu T jako typy literalne. Używany do tworzenia funkcji bezpiecznie odwołujących się do właściwości obiektu (K extends keyof T).

typeof użyty w pozycji typowej zwraca typ wartości — różni się od runtime typeof. Pozwala inferować typy z istniejących obiektów i funkcji bez duplikowania definicji. Razem tworzą potężne kombinacje: keyof typeof config.

const config = { host: 'localhost', port: 3000 }; type ConfigKey = keyof typeof config; // 'host' | 'port'
Typy zaawansowane
17 Co to jest index signature i kiedy go stosować?

Index signature ([key: string]: ValueType) opisuje obiekty o dynamicznych kluczach — słowniki, mapy, rekordy konfiguracyjne. Wszystkie wartości muszą pasować do podanego typu.

Ograniczenia: indeks string powoduje, że TypeScript traktuje wszystkie właściwości jako mogące być undefined. Bezpieczniejszą alternatywą jest utility type Record<K, V> lub Map<K, V>. Przy noUncheckedIndexedAccess: true kompilator wymusza sprawdzanie undefined przy dostępie przez indeks.

interface Dictionary { [key: string]: string; } type Scores = Record<string, number>;
Typy zaawansowane
18 Czym są overloads funkcji w TypeScript?

Function overloads pozwalają zdefiniować kilka sygnatur tej samej funkcji dla różnych kombinacji typów parametrów i zwracanych wartości. TypeScript wybiera właściwą sygnaturę w zależności od wywołania.

Składnia: przed implementacją deklaruje się sygnatury przeciążeń (bez ciała). Implementacja ma najszerszą sygnaturę i jest ukryta przed konsumentami API. Dobrze zastępuje skomplikowane union zwracane przez funkcję w jednej sygnaturze.

function parse(input: string): number; function parse(input: number): string; function parse(input: string | number): string | number { return typeof input === 'string' ? parseInt(input) : String(input); }
Typy zaawansowane
19 Jakie modyfikatory dostępu oferuje TypeScript i czym się różnią?

TypeScript oferuje trzy modyfikatory: public (domyślny — dostępny wszędzie), protected (dostępny w klasie i jej potomkach), private (tylko w klasie). Wszystkie są sprawdzane wyłącznie w czasie kompilacji.

Od TypeScript 4.3 dostępny jest też prywatny identyfikator JS (#pole) — to prawdziwa prywatność w runtime, niemożliwa do ominięcia przez rzutowanie. Różni się od private: jest nieosiągalny nawet z zewnątrz przez as any.

Klasy i OOP
20 Czym są klasy abstrakcyjne i jak różnią się od interfejsów?

Klasa abstrakcyjna (abstract class) nie może być bezpośrednio instancjonowana. Może zawierać implementacje metod (shared logic) oraz abstrakcyjne deklaracje, które muszą być zaimplementowane przez klasy pochodne.

Różnice od interfejsu: klasa abstrakcyjna istnieje w runtime jako konstruktor (można sprawdzić instanceof), może przechowywać stan i implementować metody. Interfejs jest czysto typowy — znika po kompilacji. Używaj klasy abstrakcyjnej gdy potrzebujesz wspólnej logiki; interfejsu gdy opisujesz kontrakt.

Klasy i OOP
21 Co to są mixiny w TypeScript i jak je implementować?

Mixiny to wzorzec kompozycji, który pozwala łączyć zachowania z wielu źródeł bez wielokrotnego dziedziczenia (TypeScript nie obsługuje extends A, B). Mixin to funkcja przyjmująca klasę bazową i zwracająca nową klasę ją rozszerzającą.

Implementacja opiera się na typie Constructor<T> = new (...args: any[]) => T. Każdy mixin jest funkcją wyższego rzędu. Deklaracje interfejsów scalają się z klasą, dostarczając pełną type safety. Popularny wzorzec w bibliotekach i frameworkach.

Klasy i OOP
22 Czym są gettery i settery w klasach TypeScript i kiedy ich używać?

get i set definiują akcesory pozwalające na wykonanie logiki przy odczycie lub zapisie właściwości. Z zewnątrz wyglądają jak zwykłe pole, wewnętrznie mogą walidować, obliczać lub logować.

Zastosowania: lazy initialization (obliczanie wartości przy pierwszym dostępie), walidacja danych wejściowych, właściwości pochodne (computed). Uwaga: getter bez settera tworzy właściwość tylko do odczytu. TypeScript wymaga zgodności typów między getterem a setterem od TS 4.3.

class Circle { private _radius = 0; get radius() { return this._radius; } set radius(v: number) { if (v < 0) throw new Error('Negative radius'); this._radius = v; } }
Klasy i OOP
23 Czym jest parameter properties shorthand w konstruktorze klasy?

TypeScript pozwala deklarować i inicjalizować właściwości klasy bezpośrednio w parametrach konstruktora przez dodanie modyfikatora (public, private, protected, readonly). Eliminuje powtarzalny boilerplate.

// Bez skrótu class User { public name: string; private age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } } // Ze skrótem class User { constructor(public name: string, private age: number) {} }
Klasy i OOP
24 Jak działa dziedziczenie klas w TypeScript? Czym jest super?

TypeScript obsługuje jednokrotne dziedziczenie klas przez extends. Klasa pochodna dziedziczy publiczne i chronione pola oraz metody klasy bazowej. Możliwe jest nadpisanie metod (override) z opcjonalnym słowem kluczowym override (TS 4.3).

super odwołuje się do klasy bazowej: super() wywołuje konstruktor rodzica (wymagane w konstruktorze klasy pochodnej przed użyciem this), super.metodA() wywołuje metodę bazową. Flaga noImplicitOverride wymusza jawne oznaczenie nadpisanych metod.

Klasy i OOP
25 Jak TypeScript obsługuje wielokrotną implementację interfejsów przez klasę?

Klasa może implementować wiele interfejsów przez implements A, B, C. TypeScript sprawdza, czy klasa dostarcza wszystkich wymaganych właściwości i metod z każdego interfejsu. To wyłącznie kontrola typów — brak runtime efektu.

Ważne: implements nie dziedziczy implementacji — to kontrakt, nie mixin. Klasa musi samodzielnie dostarczyć każdą metodę interfejsu. Interfejsy mogą też rozszerzać się nawzajem (interface C extends A, B).

Klasy i OOP
26 Czym jest this w TypeScript — jak typować kontekst i unikać błędów?

TypeScript pozwala typować this jako pierwszy (fałszywy) parametr funkcji: function fn(this: MyClass, ...). Kompilator sprawdza, że funkcja jest wywoływana z właściwym kontekstem. Flaga noImplicitThis wymusza jawne typowanie this gdy nie jest jasny.

Typowe problemy: metody klas przekazywane jako callbacki tracą kontekst. Rozwiązania: strzałkowe metody (method = () => {}) lub jawne bindowanie. TypeScript oferuje też typ ThisType<T> do zaawansowanego typowania obiektów konfiguracyjnych.

Klasy i OOP
27 Jak implementować wzorzec Singleton w TypeScript?

Singleton gwarantuje istnienie jednej instancji klasy. W TypeScript: konstruktor private (blokuje new z zewnątrz) + prywatna statyczna właściwość instance + publiczna statyczna metoda getInstance().

class Database { private static instance: Database; private constructor(private url: string) {} static getInstance(): Database { if (!Database.instance) Database.instance = new Database('postgres://...'); return Database.instance; } }

Alternatywa: moduły ES (import singleton — każdy import modułu zwraca tę samą instancję) lub kontenery DI (NestJS, InversifyJS).

Klasy i OOP
28 Czym są generyki (generics) w TypeScript i dlaczego są ważne?

Generyki pozwalają pisać komponenty (funkcje, klasy, interfejsy) pracujące z różnymi typami bez utraty type safety. Zamiast any używa się parametrów typowych (<T>), które są podstawiane w miejscu wywołania.

Zalety: reużywalność kodu, zachowanie informacji o typie (inaczej niż przy any), type inference — kompilator często sam wydedukuje typ bez jawnego podawania. Generyki są fundamentem całego systemu utility types TypeScript.

function identity<T>(value: T): T { return value; } const s = identity('hello'); // T = string, s: string
Generyki i utility types
29 Czym są generic constraints i jak je stosować?

Generic constraints (T extends SomeType) ograniczają jakie typy mogą być użyte jako parametr generyczny. Pozwalają kompilatorowi wiedzieć, jakie właściwości lub metody są dostępne na T.

Typowe zastosowania: T extends object, T extends keyof U, T extends { length: number }. Constraints można łączyć z extends i &. Klauzula extends w generykach różni się od dziedziczenia klas — to warunek przypisywalności.

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; // bezpieczny dostęp do właściwości }
Generyki i utility types
30 Omów najważniejsze wbudowane utility types w TypeScript.

TypeScript dostarcza wiele gotowych utility types opartych na mapped types i conditional types:

  • Partial<T> — wszystkie pola opcjonalne
  • Required<T> — wszystkie pola wymagane
  • Readonly<T> — wszystkie pola readonly
  • Pick<T, K> / Omit<T, K> — wybierz lub pomiń pola
  • Record<K, V> — słownik kluczy K i wartości V
  • Exclude<T, U> / Extract<T, U> — filtrowanie unii
  • NonNullable<T> — usuwa null/undefined
  • ReturnType<F> / Parameters<F> — typy z sygnatur funkcji
  • Awaited<T> — rozpakowuje zagnieżdżone Promise
Generyki i utility types
31 Czym jest infer w conditional types?

Słowo kluczowe infer w conditional types pozwala „przechwycić" i nazwać fragment typu, który jest dopasowywany. Używane wyłącznie wewnątrz klauzuli extends conditional type.

To mechanizm stojący za utility types takimi jak ReturnType, Parameters, InstanceType czy Awaited. Pozwala wyciągać dowolnie głęboko zagnieżdżone typy bez dostępu do oryginalnej definicji.

// ReturnType samodzielnie zaimplementowany type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
Generyki i utility types
32 Czym są variadic tuple types w TypeScript?

Variadic tuple types (TS 4.0) pozwalają na spread elementów krotki w definicji innej krotki: [...T, string], [boolean, ...T]. TypeScript zachowuje pełną informację o typach przy konkatenacji krotek.

Zastosowania: typowanie funkcji concat, zip, pipelines, currying. Pozwala na precyzyjne typowanie funkcji variadycznych bez uciekania się do overloadów dla każdej liczby argumentów.

type Concat<T extends any[], U extends any[]> = [...T, ...U]; type Result = Concat<[number, string], [boolean]>; // [number, string, boolean]
Generyki i utility types
33 Jak typować funkcje wyższego rzędu (higher-order functions) w TypeScript?

Funkcje wyższego rzędu przyjmują lub zwracają inne funkcje. Typowanie opiera się na generykach i typach funkcji: (...args: A) => R. Klucz to zachowanie informacji typowej przez całą transformację.

function map<T, U>(arr: T[], fn: (item: T) => U): U[] { return arr.map(fn); } function compose<A, B, C>(f: (b: B) => C, g: (a: A) => B): (a: A) => C { return a => f(g(a)); }

Przy curryingu i pipelines przydają się variadic tuple types i conditional types do prawidłowego inferowania typów zwracanych w każdym kroku.

Generyki i utility types
34 Jak typować odpowiedzi API — najlepsze praktyki?

Najlepsze podejście to schema validation + type inference: biblioteki jak zod, valibot lub arktype definiują schemę walidującą dane w runtime i automatycznie inferują typy TypeScript.

Unikaj as ResponseType (type assertion bez walidacji) — to fałszywe poczucie bezpieczeństwa. Zamiast tego: zdefiniuj schemat Zod → z.infer<typeof schema> daje typ → parsuj odpowiedź przez schema.parse(data). Błędy walidacji wyrzucane są w runtime, zanim dane trafią do aplikacji. Alternatywa: generowanie typów z OpenAPI (openapi-ts).

Generyki i utility types
35 Czym jest covariance i contravariance w systemie typów TypeScript?

Kowariancja — typ pochodny może być użyty tam gdzie oczekiwany jest typ bazowy (pozycja zwracana funkcji). Kontrawariancja — odwrotnie: tam gdzie oczekiwany jest typ bardziej szczegółowy, można podstawić ogólniejszy (pozycja parametrów funkcji).

TypeScript stosuje bivariance dla metod klas (historyczny kompromis), ale strict function types (strictFunctionTypes: true) wprowadza kontrawariancję dla typów funkcji. Oznacza to, że (animal: Animal) => void nie jest przypisywalny tam gdzie oczekiwane (dog: Dog) => void.

Generyki i utility types
36 Czym jest satisfies operator w TypeScript?

Operator satisfies (TS 4.9) sprawdza, czy wyrażenie pasuje do podanego typu, ale nie zmienia inferred type zmiennej. Różni się od adnotacji (która narzuca typ) i od as (która nie sprawdza nic).

Praktyczne zastosowanie: weryfikacja konfiguracji zachowując dokładniejszy inferred type. Np. obiekt sprawdzony przez satisfies Record<string, string> zachowuje literalne typy kluczy — IDE oferuje autocomplete na oryginalnych kluczach, a TypeScript pilnuje struktury.

const config = { port: 3000, host: 'localhost' } satisfies { port: number; host: string }; // config.port: 3000 (literal), nie number
Generyki i utility types
37 Jakie są najważniejsze opcje w tsconfig.json?

Kluczowe opcje tsconfig.json:

  • strict: true — włącza zestaw flag: strictNullChecks, strictFunctionTypes, noImplicitAny itd. — zalecane w każdym projekcie
  • target — docelowa wersja JS (es2020, esnext)
  • module / moduleResolution — system modułów (commonjs, esm, bundler)
  • outDir / rootDir — lokalizacje plików
  • paths — aliasy importów
  • lib — dostępne typy (DOM, ES2022)
  • noUncheckedIndexedAccess — bezpieczeństwo dostępu przez indeks
  • exactOptionalPropertyTypes — precyzyjna opcjonalność
Praktyka i konfiguracja
38 Czym są pliki deklaracji (.d.ts) i do czego służą?

Pliki .d.ts to deklaracje typów — zawierają wyłącznie informacje typowe bez implementacji. Pozwalają TypeScriptowi rozumieć kształt bibliotek napisanych w czystym JavaScript.

Źródła: wbudowane w paczkę npm (pole types w package.json), repozytorium DefinitelyTyped (pakiety @types/*) lub wygenerowane przez kompilator TS (declaration: true). Pisząc własną bibliotekę zawsze generuj pliki .d.ts, by konsumenci mieli type safety.

Praktyka i konfiguracja
39 Jak działa module augmentation i declaration merging?

Declaration merging pozwala TypeScriptowi scalać wielokrotne deklaracje tej samej nazwy. Interfejsy o tej samej nazwie automatycznie łączą swoje właściwości. Klasy i interfejsy o tej samej nazwie też się scalają.

Module augmentation umożliwia rozszerzanie typów z zewnętrznych modułów bez modyfikacji oryginałów. Składnia: declare module 'express' { ... }. Typowe zastosowanie: dodawanie właściwości do Request Express, rozszerzanie typów React (np. JSX.IntrinsicElements), augmentacja globalnych typów.

Praktyka i konfiguracja
40 Jak TypeScript integruje się z React — JSX, typy komponentów i hooki?

Pliki .tsx obsługują JSX. Typy React dostarczane przez @types/react. Komponenty typuje się przez React.FC<Props> (lub bezpośrednio jako funkcję z typowanymi parametrami — preferowane). Props definiowane przez interface lub type.

Hooki są generyczne: useState<number>, useRef<HTMLDivElement>, useContext<Theme>. useReducer typuje się przez discriminated union akcji. Własne hooki zwracają dobrze otypowane wartości — opcjonalnie as const dla krotek zwracanych z hooków zamiast tablic.

Praktyka i konfiguracja
41 Czym jest strict mode TypeScript i co obejmuje?

Flaga strict: true w tsconfig włącza całą rodzinę flag naraz:

  • strictNullChecksnull i undefined nie są przypisywalne do innych typów
  • noImplicitAny — brak ukrytych any
  • strictFunctionTypes — kontrawariancja parametrów funkcji
  • strictBindCallApply — typowanie bind/call/apply
  • strictPropertyInitialization — właściwości klas muszą być inicjalizowane
  • noImplicitThis — brak ukrytego this: any
  • useUnknownInCatchVariablese w catch jest unknown
Praktyka i konfiguracja
42 Jak migrować istniejący projekt JavaScript do TypeScript?

Zalecana strategia stopniowej migracji: 1) Dodaj tsconfig.json z allowJs: true i checkJs: false — TS kompiluje JS bez błędów. 2) Zmieniaj pliki z .js na .ts jeden po drugim — zacznij od modułów bez zależności (leaf files). 3) Stopniowo zwiększaj rygoryzm — wyłącz allowJs, włącz strict.

Narzędzia: ts-migrate (automatyczne dodawanie any żeby projekt się kompilował), JSDoc types (typy w komentarzach zanim zmienisz rozszerzenie), @ts-check w plikach JS. Unikaj masowego używania any — to dług techniczny do spłacenia.

Praktyka i konfiguracja
43 Jak TypeScript obsługuje asynchroniczność — async/await i typy?

Funkcje async automatycznie zwracają Promise<T>, gdzie T to typ wartości przez nie zwróconej. TypeScript inferuje to automatycznie lub można podać jawnie: async function fetchUser(): Promise<User>.

Utility type Awaited<T> (TS 4.5) rekurencyjnie rozpakowuje zagnieżdżone Promise — przydatny do typowania wyników Promise.all. Przy obsłudze błędów w catch zmienna e jest unknown (strict mode) — wymagane type guard lub instanceof Error.

async function fetchUser(id: number): Promise<User> { const res = await fetch(`/api/users/${id}`); return res.json() as Promise<User>; }
Praktyka i konfiguracja
44 Czym jest Project References w TypeScript i kiedy go używać?

Project References (TS 3.0) pozwalają podzielić duży projekt na mniejsze podprojekty z oddzielnymi plikami tsconfig.json. Kompilacja jest przyrostowa — tylko zmienione projekty są rekompilowane.

Idealne dla monorepo: oddzielne pakiety (shared, backend, frontend) mogą na siebie wskazywać przez references w tsconfig. Wymaga composite: true w projektach-zależnościach. Narzędzia: Turborepo, Nx, pnpm workspaces + TS project references to sprawdzona kombinacja.

Praktyka i konfiguracja
45 Jakie są najczęstsze antypatyczne wzorce TypeScript i jak ich unikać?

Najczęstsze antypatyczne wzorce w TypeScript:

  • Nadużywanie any — wyłącza type checking; używaj unknown + type guard
  • Type assertions bez walidacji (data as User) — fałszywe bezpieczeństwo; waliduj dane zewnętrzne przez Zod lub podobne
  • Non-null assertion (!) wszędzie — ukrywa prawdziwe null bugs; zamiast tego jawna obsługa null/undefined
  • Interfejsy zamiast union dla stanów — zamiast flag boolean używaj discriminated unions
  • Ignorowanie błędów przez // @ts-ignore — zamiast tego @ts-expect-error z komentarzem, lub napraw błąd
  • Brak strict: true — bez tego TypeScript jest zbyt pobłażliwy
Praktyka i konfiguracja