Menu

Tryb strict w TypeScript: co włącza strict: true

strict: true w tsconfig.json włącza całą rodzinę sprawdzeń typów: noImplicitAny, strictNullChecks, strictPropertyInitialization i pięć kolejnych. Zobacz, co wyłapuje każde z nich, jak włączyć tryb strict w istniejącym projekcie i jakie przydatne flagi nie należą do strict.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Tryb strict to opcja "strict": true w tsconfig.json. To jeden przełącznik dla ośmiu flag sprawdzania typów, które wyłapują niejawne any, niesprawdzone null i undefined, niebezpieczne przypisania funkcji oraz niezainicjowane pola klas. W TypeScript 7 strict jest domyślnie włączony. Tak wygląda kod, który przechodzi te sprawdzenia:

Wynik to no user, SyntaxError i 42. Usuń : number, ?. albo sprawdzenie instanceof, a plik przestanie się kompilować.

Jak go włączyć

{
    "compilerOptions": {
        "strict": true
    }
}

Pojedyncze flagi mają pierwszeństwo przed strict, w obie strony. "strict": true, "strictNullChecks": false zostawia wszystko poza sprawdzaniem null, a "strict": false, "noImplicitAny": true włącza tylko tę jedną flagę. strict oznacza też zgodę na sprawdzenia dodane w przyszłych wersjach, bo nowe flagi z rodziny strict dołączają do grupy.

strict w TypeScript nie ma nic wspólnego z dyrektywą "use strict" w JavaScript, która jest trybem działania w czasie wykonania. TypeScript 7 zawsze emituje "use strict" tam, gdzie to potrzebne, a ustawienie alwaysStrict: false to błąd TS5108 (opcję usunięto).

Co wyłapuje każda flaga

FlagaCo zgłaszaBłąd
noImplicitAnyparametr lub zmienną, której typ po cichu byłby anyTS7006 Parameter 'x' implicitly has an 'any' type.
strictNullChecksużycie wartości, która może być null lub undefinedTS18048 'u' is possibly 'undefined'.
strictFunctionTypesprzypisanie funkcji, której typ parametru jest węższy niż wymaganyTS2322
strictBindCallApplyzłe argumenty dla .call, .bind i .applyTS2345
strictPropertyInitializationpole klasy, któremu nigdy nie przypisano wartościTS2564 Property 'name' has no initializer and is not definitely assigned in the constructor.
noImplicitThisthis z niejawnym typem any, na przykład w zagnieżdżonej functionTS2683
useUnknownInCatchVariablesużycie zmiennej z catch przed jej zawężeniem (ma typ unknown)TS18046 'err' is of type 'unknown'.
strictBuiltinIteratorReturntraktowanie it.next().value wbudowanego iteratora jak zawsze zdefiniowanegoTS2322

Najwięcej kodu zmieniają dwie flagi: noImplicitAny i strictNullChecks. Ten blok celowo łamie obie:

Wypisuje index.ts(2,17): error TS7006: Parameter 'x' implicitly has an 'any' type. oraz index.ts(11,13): error TS18048: 'user' is possibly 'undefined'. Bez strict ten kod się kompiluje, a potem pada w czasie wykonania z TypeError: Cannot read properties of undefined (reading 'name'). Ze strict awaria zamienia się w błąd kompilacji. Poprawki to pierwszy blok na tej stronie.

strictFunctionTypes: po co istnieje

Funkcja, która obsługuje tylko napisy, nie może być użyta tam, gdzie mogą trafić liczby. Poniższe sprawdzenie jest wyciszone przez @ts-expect-error, żeby można było je uruchomić i zobaczyć, przed czym chroni błąd:

Bez komentarza przypisanie to błąd TS2322, Type '(s: string) => void' is not assignable to type 'Handler'., a po nim Types of parameters 's' and 'value' are incompatible. Celowo zostawiono jeden wyjątek: parametry metod zadeklarowanych składnią metody (handle(value: string | number): void wewnątrz interfejsu) wciąż są sprawdzane łagodniej, więc shout dałoby się przypisać do takiej metody bez błędu.

strictPropertyInitialization

Każde pole klasy musi dostać wartość w deklaracji albo w konstruktorze. Są trzy sposoby, żeby to spełnić:

Wynik:

Account {
  owner: 'Ada',
  balance: 0,
  history: [],
  lastLogin: undefined,
  sessionId: 's-1'
}

Właściwości lastLogin i sessionId istnieją z wartością undefined, bo przy targecie ES2022 pola klas są prawdziwymi polami JavaScriptu. ! usuwa sprawdzenie, nie dodając żadnej ochrony w czasie wykonania, więc lepiej wybierać pozostałe formy. Ta flaga wymaga strictNullChecks: wyłączenie tamtej wyłącza też tę.

Włączanie strict w istniejącym projekcie

Przełączenie dużego projektu na strict naraz może dać setki błędów. Ponieważ strict jest domyślny w TypeScript 7, aktualizacja projektu, którego tsconfig.json nigdy nie wspominał o strict, sama go włącza. Napisz "strict": false, jeśli na czas migracji potrzebujesz starego zachowania. Droga, na której build cały czas przechodzi:

  1. Dodaj "strict": true i wyłącz flagi z największą liczbą błędów, zwykle "strictNullChecks": false i "noImplicitAny": false.
  2. Popraw pozostałe błędy, potem włącz kolejną flagę i powtórz.
  3. Przy noImplicitAny większość poprawek to adnotacje parametrów. Przy strictNullChecks dodaj | undefined tam, gdzie wartości może brakować, a potem obsłuż to przez ?., ?? albo sprawdzenie if.
  4. Tam, gdzie poprawka musi poczekać, dopisz w linii // @ts-expect-error z uzasadnieniem. W przeciwieństwie do @ts-ignore zgłasza on błąd, gdy problem zniknie, więc lista sama się skraca.

Nie wyciszaj błędów hurtowo przez as any albo !: każde takie użycie ukrywa dokładnie ten błąd, do którego wyłapywania dodano flagę.

Przydatne flagi spoza strict

Są osobno, bo odrzucają kod, który często jest poprawny. Wiele projektów i tak je włącza.

FlagaCo robi
noUncheckedIndexedAccessarr[i] i record[key] mają w typie undefined
exactOptionalPropertyTypesdebug?: boolean przyjmuje brak klucza, ale nie debug: undefined
noImplicitReturnskażda ścieżka funkcji zwracającej wartość musi coś zwrócić (TS7030)
noImplicitOverridemetoda nadpisująca metodę bazową musi mieć override (TS4114)
noFallthroughCasesInSwitchniepusty case musi kończyć się break, return albo throw (TS7029)
noUnusedLocals, noUnusedParametersnieużywane zmienne i parametry są błędami
noPropertyAccessFromIndexSignatureklucze z sygnatury indeksu wymagają obj["key"], a nie obj.key

Najwięcej prawdziwych błędów wyłapuje noUncheckedIndexedAccess. Gdy jest włączona:

const scores = [90, 85];
const d: Record<string, number> = {};

const third: number = scores[2]; // error TS2322: Type 'number | undefined' is not assignable to type 'number'.
const c: number = d["x"];        // same error

const safe = scores[2] ?? 0;     // number
for (const s of scores) {        // for...of is not affected: s is number
    console.log(s);
}

tsc --init w TypeScript 7 włącza w generowanej konfiguracji noUncheckedIndexedAccess i exactOptionalPropertyTypes, obok strict.

Najczęściej zadawane pytania

Co robi tryb strict w TypeScript?

"strict": true włącza grupę ostrzejszych sprawdzeń: noImplicitAny, strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitThis, useUnknownInCatchVariables i strictBuiltinIteratorReturn. Razem nie pozwalają, by any pojawiało się po cichu, i sprawiają, że null oraz undefined stają się częścią systemu typów.

Czy tryb strict jest domyślnie włączony w TypeScript?

W TypeScript 7 tak: strict ma domyślnie wartość true, więc projekt bez ustawienia strict dostaje wszystkie sprawdzenia strict. tsc --init też jawnie zapisuje "strict": true. Żeby z tego zrezygnować, trzeba napisać "strict": false.

Czy mogę wyłączyć jedno sprawdzenie strict, a resztę zostawić?

Tak. Pojedyncze flagi mają pierwszeństwo przed strict: { "strict": true, "strictNullChecks": false } zostawia każde sprawdzenie strict poza sprawdzaniem null. Tak zwykle migruje się duży projekt, flaga po fladze.

Czy tryb strict w TypeScript to to samo co "use strict" w JavaScript?

Nie. "use strict" to tryb działania JavaScriptu, który zmienia zachowanie kodu w czasie wykonania. strict w TypeScript zmienia tylko to, co zgłasza sprawdzanie typów. TypeScript 7 zawsze emituje "use strict" dla wyjścia, które nie jest modułem, a alwaysStrict: false to teraz usunięta opcja.

Czy strict obejmuje noUncheckedIndexedAccess?

Nie. noUncheckedIndexedAccess, exactOptionalPropertyTypes, noImplicitReturns, noImplicitOverride i noFallthroughCasesInSwitch to osobne flagi, które włączasz samodzielnie. tsc --init włącza pierwsze dwie w generowanej konfiguracji.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ