Funkcja w TypeScript to funkcja JavaScript z typami parametrów i opcjonalnie z typem wartości zwracanej. Każdy parametr dostaje adnotację, typ zwracany stoi po liście parametrów, a kompilator sprawdza względem nich każde wywołanie.
Oba błędne wywołania to błędy kompilacji (TS2345 i TS2554). Komentarze // @ts-expect-error mówią kompilatorowi, że w następnej linii spodziewany jest błąd, więc reszta pliku nadal się uruchamia. Usuń jeden z nich i uruchom ponownie, aby zobaczyć prawdziwy komunikat.
Typy parametrów
Każdy parametr zapisuje się jako name: Type. Przy strict, który w TypeScript 7 jest domyślnie włączony, parametr bez adnotacji i bez kontekstu do wnioskowania to błąd: Parameter 'x' implicitly has an 'any' type. (TS7006). W praktyce więc każdy parametr samodzielnej funkcji ma adnotację.
Jako typ parametru działa każdy typ: prymitywy, tablice, typy obiektowe, unie, inne funkcje.
TypeScript sprawdza też liczbę argumentów. Przekazanie większej lub mniejszej liczby, niż deklaruje funkcja, to błąd, w odróżnieniu od zwykłego JavaScriptu, który brakujące uzupełnia wartością undefined, a nadmiarowe ignoruje. Jak uczynić parametr opcjonalnym lub nadać mu wartość domyślną, opisuje strona o parametrach opcjonalnych.
Typy zwracane: adnotacja czy wnioskowanie
Typ zwracany stoi po nawiasie zamykającym: function f(): Type. Jest opcjonalny. Bez niego TypeScript wnioskuje typ z każdego return w ciele funkcji.
Adnotacja się opłaca, gdy funkcja jest eksportowana, ma kilka ścieżek zwracania albo jest rekurencyjna. Błąd pojawia się wtedy wewnątrz funkcji, przy złym return, a nie u wywołującego. Klasyczny przypadek to gałąź, w której brakuje return:
Kompilator zgłasza index.ts(2,32): error TS2366: Function lacks ending return statement and return type does not include 'undefined'. Dodaj na końcu return "C";, a kod się uruchomi. Bez adnotacji : string funkcja skompilowałaby się z wywnioskowanym typem "A" | "B" | undefined, a problem przeniósłby się do tego, kto używa wyniku.
void: funkcje, które nic nie zwracają
Funkcja, która tylko coś robi (loguje, zapisuje, modyfikuje), ma typ zwracany void. TypeScript wnioskuje go, gdy nie ma return z wartością, i można go też zapisać jawnie.
Te reguły dotyczące void często zaskakują:
| Sytuacja | Dozwolone? |
|---|---|
function f(): void { return 42; } | Nie: Type 'number' is not assignable to type 'void'. (TS2322) |
const f: () => void = () => 42; | Tak: wartość jest zwracana, ale wywołujący nie mogą na niej polegać |
function f(): undefined {} | Tak (TypeScript 5.1 i nowsze) |
Użycie wyniku funkcji void w if | Nie: An expression of type 'void' cannot be tested for truthiness. (TS1345) |
Drugi wiersz jest celowy. Dzięki niemu arr.forEach(x => list.push(x)) się kompiluje, choć push zwraca liczbę. void w typie funkcji znaczy „cokolwiek to zwraca, nikt tego nie czyta”. Dla funkcji, która w ogóle nie wraca (zawsze rzuca wyjątek albo działa w nieskończonej pętli), typem zwracanym jest never.
Funkcje strzałkowe i wyrażenia funkcyjne
Funkcje strzałkowe i wyrażenia funkcyjne przyjmują te same adnotacje. Typ zwracany stoi po liście parametrów, przed =>.
Bez nawiasów (x, y) => { x, y } jest blokiem bez return, a funkcja zwraca undefined. Reguły działania funkcji strzałkowych w czasie wykonania (brak własnego this, brak arguments) są takie same jak w JavaScripcie; zobacz funkcje strzałkowe w JavaScript.
Gdy funkcja jest zapisana bezpośrednio jako argument, zwykle w ogóle nie dodaje się adnotacji jej parametrów. TypeScript wie, co otrzymuje callback, z funkcji, do której jest przekazywany (typowanie kontekstowe): w [1, 2].map(n => n * 2) n ma już typ number. Zapisywanie typu funkcji jako wartości, (n: number) => string, opisuje strona o typach funkcji.
Zwracanie kilku wartości
Funkcja zwraca jedną wartość, więc zwróć obiekt albo krotkę i ją zdestrukturyzuj.
Typ zwracany krotki wymaga adnotacji. Bez niej [a, b] jest wnioskowane jako string[]: destrukturyzacja nadal działa, ale typ nie mówi już, że elementy są dokładnie dwa.
Funkcje async
Funkcja async zawsze zwraca Promise. Zapisz typ zwracany jako Promise<T>, gdzie T to to, co funkcja zwraca w środku.
async function loadScore(id: number): Promise<number> {
await new Promise((resolve) => setTimeout(resolve, 10));
return id * 10;
}
async function main(): Promise<void> {
const score = await loadScore(4); // score: number
console.log("score", score);
}
main();
Zapis async function f(): number to błąd: The return type of an async function or method must be the global Promise<T> type. Did you mean to write 'Promise<number>'? (TS1064).
Parametr this
JavaScript ustala this w chwili wywołania. TypeScript pozwala zadeklarować, czym musi być this, za pomocą sztucznego pierwszego parametru o nazwie this. Jest on usuwany ze skompilowanego JavaScriptu, a wywołujący go nie przekazują.
interface Counter {
count: number;
}
function increment(this: Counter, by: number): void {
this.count += by;
}
const c = { count: 0, increment };
c.increment(2); // fine: this is c
increment(2);
// error TS2684: The 'this' context of type 'void' is not assignable to method's 'this' of type 'Counter'.
Bez parametru this wewnątrz samodzielnej funkcji this jest niejawnym any, a tryb strict zgłasza 'this' implicitly has type 'any' because it does not have a type annotation. (TS2683). W metodach klas this ma już typ instancji klasy, więc ten parametr przydaje się głównie w samodzielnych funkcjach dołączanych do obiektów i w callbackach, które biblioteki wywołują z określonym this.
Najczęściej zadawane pytania
Jak określić typ zwracany funkcji w TypeScript?
Po liście parametrów napisz dwukropek i typ: function total(a: number, b: number): number { ... }. W funkcji strzałkowej trafia w to samo miejsce: const total = (a: number, b: number): number => a + b;. Jeśli go pominiesz, TypeScript wywnioskuje typ zwracany z instrukcji return.
Czy zawsze trzeba dodawać adnotację typu zwracanego w TypeScript?
Nie, bo typ zwracany jest wnioskowany. Dodawaj go w funkcjach eksportowanych i w funkcjach z kilkoma ścieżkami return: adnotacja dokumentuje kontrakt, a błędny return jest wtedy zgłaszany wewnątrz funkcji, a nie u jakiegoś odległego wywołującego.
Czym różni się void od undefined w TypeScript?
void znaczy „wywołujący nie powinien używać wartości zwracanej”. undefined to konkretny typ wartości. Funkcja zadeklarowana jako (): void nie może zwrócić wartości, ale typ callbacku () => void przyjmuje funkcję, która coś zwraca, a wynik jest po prostu ignorowany. Używaj void dla funkcji, które nic nie zwracają, a undefined tylko wtedy, gdy wywołujący naprawdę porównuje wynik z undefined.
Jak zwrócić kilka wartości z funkcji w TypeScript?
Zwróć obiekt ({ min: number; max: number }) albo krotkę ([number, number]) i zdestrukturyzuj ją w miejscu wywołania. Obiekty są czytelniejsze, gdy wartości mają różne znaczenia; krotki dobrze sprawdzają się przy krótkich parach, takich jak [value, setValue].
Jaki typ zwraca funkcja async w TypeScript?
Zawsze Promise. Funkcja async, która zwraca number, ma typ zwracany Promise<number>, a zapis : number jest w niej błędem kompilacji (TS1064). Funkcja, która nic nie zwraca, ma typ Promise<void>.