Menu

Wnioskowanie typów w TypeScript: kiedy dodać adnotację

TypeScript ustala większość typów na podstawie wartości, które piszesz. Zobacz, co wnioskuje dla zmiennych, let i const, obiektów, tablic i wartości zwracanych, jak callbacki dostają typy z kontekstu i gdzie nadal potrzebna jest adnotacja.

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

Wnioskowanie typów to TypeScript ustalający typ, którego nie napisano. Zainicjuj zmienną, a przyjmie typ wartości. Zwróć wartość z funkcji, a typ zwracany wynika z niej sam. Większość kodu w TypeScript ma dużo mniej adnotacji, niż się ludziom wydaje.

Komentarze pokazują, co wyświetla edytor po najechaniu na każdą nazwę. Ostatnia linia to błąd kompilacji (TS2322), mimo że count nigdy nie dostało adnotacji. // @ts-expect-error oznacza go jako oczekiwany, więc reszta bloku się wykonuje.

let a const: poszerzanie literałów

const nigdy się nie zmienia, więc TypeScript daje mu najwęższy typ: samą wartość literału. let dostaje szerszy typ, bo później można mu przypisać coś innego.

let a = "left";    // string
const b = "left";  // "left"
let c = 42;        // number
const d = 42;      // 42
const e = true;    // true

Ma to znaczenie, gdy funkcja przyjmuje tylko określone wartości:

index.ts(7,6): error TS2345: Argument of type 'string' is not assignable to parameter of type '"left" | "right"'.

Popraw to przez const albo przez adnotację zmiennej: let dir: "left" | "right" = "left". Typy literałów mają własną stronę, typy literałów.

Obiekty i tablice poszerzają swoje elementy

Właściwości literału obiektu są modyfikowalne, więc poszerzają się jak let, nawet gdy sam obiekt jest const. Elementy tablic zachowują się tak samo. as const czyni wszystko readonly i zachowuje typy literałów.

Wnioskowanie typu zwracanego

Typ zwracany funkcji jest wnioskowany z jej instrukcji return. Gdy jest ich kilka, wynikiem jest ich unia.

Wywnioskowane typy zwracane wystarczają w większości funkcji. Pisz typ zwracany jawnie w eksportowanych albo publicznych funkcjach, gdy chcesz, żeby sygnatura była kontraktem: zmiana w ciele, która zwraca inny typ, zgłosi wtedy błąd w samej funkcji, a nie u jakiegoś odległego wywołującego.

Typowanie kontekstowe: callbacki dostają typy

Parametry nie są wnioskowane z tego, jak funkcja jest wywoływana. Są dwa wyjątki. Parametr z wartością domyślną przyjmuje typ tej wartości (function retry(times = 3) sprawia, że times to number). A funkcja napisana tam, gdzie już oczekiwany jest typ funkcji, dostaje typy parametrów z tego kontekstu, więc parametry callbacków nie potrzebują adnotacji.

To samo dotyczy forEach, filter, reduce, callbacków Promise, obsługi zdarzeń i każdej funkcji przekazywanej do parametru z zadeklarowanym typem funkcji.

Parametry potrzebują adnotacji

Bez wartości domyślnej albo takiego kontekstu TypeScript nie patrzy na to, jak funkcja jest wywoływana, żeby zgadnąć typy parametrów. Przy włączonym strict parametr bez adnotacji to błąd TS7006:

index.ts(2,17): error TS7006: Parameter 'x' implicitly has an 'any' type.

Napisz function double(x: number), a blok wypisze 42, z typem zwracanym number wywnioskowanym z x * 2.

Najlepszy wspólny typ

Dla literału tablicy z różnymi rodzajami wartości TypeScript wnioskuje tablicę unii ich typów. Nie szuka wspólnego typu bazowego, którego nie wymieniono.

Jedyny przypadek, na który trzeba uważać, to pusta tablica. const list = [] zaczyna jako "ewoluująca" tablica, której typ rośnie z każdym push. Adnotacja const list: string[] = [] od razu mówi, o co chodzi, i jest czytelniejsza.

Kiedy dodać adnotację

SytuacjaAdnotacja?
Zmienna zainicjowana wartościąNie, typ wynika z wartości
Zmienna zadeklarowana bez wartości (let result;)Tak, let result: string;
Parametry funkcjiTak (poza callbackami z kontekstem albo wartością domyślną)
Typ zwracany małej wewnętrznej funkcjiOpcjonalnie
Typ zwracany eksportowanej albo publicznej funkcjiZalecana, jako kontrakt
Pusta tablica albo obiekt wypełniany późniejTak, const ids: number[] = []
Wartość ma zachować typy literałówUżyj as const
Wartość musi pasować do typu, ale zachować własny wywnioskowany typUżyj satisfies

Najszybszy sposób, żeby zobaczyć, co zostało wywnioskowane, to najechać na nazwę w edytorze. Bez edytora przypisz wartość do zmiennej o złym typie, na przykład const probe: boolean = mixed;, a komunikat błędu kompilatora poda wywnioskowany typ.

Najczęściej zadawane pytania

Czym jest wnioskowanie typów w TypeScript?

To kompilator ustalający typ, którego nie napisano. let count = 0 daje count typ number, a funkcja, która zwraca a + b dla dwóch liczb, dostaje typ zwracany number. Wywnioskowany typ jest sprawdzany dokładnie tak jak napisany.

Czy w TypeScript trzeba dodawać adnotację do każdej zmiennej?

Nie. Dodawaj adnotacje do parametrów funkcji (nie są wnioskowane z wywołań, tylko z wartości domyślnej albo z kontekstu callbacka), do typów zwracanych eksportowanych funkcji, jeśli chcesz stabilnego API, i do zmiennych zadeklarowanych bez wartości. Resztę niech wywnioskuje TypeScript: const name: string = "Ada" powtarza to, co kompilator już wie.

Dlaczego const wnioskuje typ literału, a let wnioskuje string?

Do const nigdy nie można przypisać nowej wartości, więc const dir = "left" dostaje typ literału "left". let może później przechowywać dowolny inny string, więc let dir = "left" jest poszerzane do string. To samo poszerzanie dotyczy właściwości literału obiektu, bo właściwościom można przypisać nowe wartości. as const to zatrzymuje.

Dlaczego TypeScript mówi, że parametr ma niejawnie typ 'any'?

To błąd TS7006 z noImplicitAny, które należy do strict. TypeScript nie wnioskuje typów parametrów z tego, jak funkcja jest wywoływana, więc goły parametr miałby typ any. Dodaj adnotację, function double(x: number), albo przekaż funkcję tam, gdzie jej typ wynika z kontekstu, na przykład jako callback do map.

Jak zobaczyć typ wywnioskowany przez TypeScript?

Najedź kursorem na nazwę w edytorze (VS Code i większość edytorów pokazuje wywnioskowany typ w dymku). W kodzie przypisanie wartości do zmiennej o oczywiście złym typie sprawia, że kompilator wypisze wywnioskowany typ w komunikacie błędu.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ