W TypeScript promise ma typ Promise<T>, gdzie T to typ wartości, którą się rozwiązuje. Funkcja zwracająca promise deklaruje to w typie zwracanym, a wartość, którą dostajesz z then lub await, ma typ T.
Mechanika promise'ów (stany, kolejka mikrozadań, łańcuchowanie) to zwykły JavaScript, omówiony w JavaScript promises. Ta strona dotyczy typów.
Typowanie new Promise i resolve
Gdy new Promise jest wartością zwracaną funkcji z zadeklarowanym typem zwracanym, T pochodzi stamtąd. Samodzielnie TypeScript nie wnioskuje T z wywołań resolve i dostajesz Promise<unknown>. Przekaż argument typu jawnie:
Dwa szczegóły. resolve jest typowane na podstawie T, więc resolve("42") w Promise<number> to błąd kompilacji. A resolve() bez argumentu jest dozwolone tylko wtedy, gdy T zawiera void: w Promise<number> to błąd TS2794 (Expected 1 arguments, but got 0. Did you forget to include 'void' in your type argument to 'Promise'?).
Strona reject nie jest typowana. Nie ma Promise<T, E>: promise może zostać odrzucony dowolną wartością.
then, catch i finally
Każde then zwraca nowy promise o typie tego, co zwraca jego callback. Jeśli callback zwraca promise, TypeScript go rozpakowuje, więc nigdy nie dostajesz Promise<Promise<T>>.
Parametr callbacku catch ma w bibliotece standardowej typ any, a nie unknown. Nic nie powstrzyma cię przed napisaniem e.message na wartości, która może być stringiem. Oznacz go jako unknown, jak wyżej, i zawęź przed użyciem. try/catch wokół await radzi sobie lepiej: przy strict jego zmienna catch już jest unknown (zobacz obsługę błędów).
Promise.all zwraca krotkę
Dla literału tablicy z promise'ami różnych typów Promise.all zwraca promise krotki, w której każda pozycja zachowuje własny typ:
Można też wmieszać wartości niebędące promise'ami, przechodzą bez zmian. Promise.all odrzuca, gdy tylko jedno wejście zostanie odrzucone, a pozostałe wyniki przepadają. Promise.race rozwiązuje się lub odrzuca wynikiem pierwszego zakończonego i ma typ unii wejść; Promise.any rozwiązuje się pierwszym sukcesem i odrzuca z AggregateError tylko wtedy, gdy zawiodą wszystkie wejścia.
Promise.allSettled i typ jego wyniku
Promise.allSettled czeka na każde wejście i nigdy nie odrzuca. Każdy wynik to PromiseSettledResult<T>, unia, którą zawężasz polem status:
r.value istnieje dopiero po sprawdzeniu status, bo wariant odrzucony nie ma value. reason to any, z tego samego powodu co w catch. filter używa predykatu typu, żeby przefiltrowana tablica miała typ spełnionych wyników.
Opakowywanie API z callbackami w promise
Starsze API zgłaszają wyniki przez callback, często w stylu Node (err, result) => void. Opakuj je raz w funkcję zwracającą otypowany promise, a reszta kodu może używać await:
W Node util.promisify robi to dla funkcji zgodnych z konwencją (err, result), a wiele wbudowanych modułów ma już wersje oparte na promise'ach (node:fs/promises, node:timers/promises).
Typowe błędy
- Zwracanie
Ttam, gdzie sygnatura mówiPromise<T>, lub odwrotnie. Funkcja nie-async zadeklarowana jakoPromise<User>musi zwrócić promise; funkcjaasynczwraca go automatycznie. - Zapominanie o obsłudze promise'a. Wywołanie takie jak
save(user);bezawait,thenczycatchkompiluje się bez problemu, a odrzucenie staje się nieobsłużonym odrzuceniem (które domyślnie kończy proces Node). Wyłapuje to reguła typescript-eslintno-floating-promises. - Ufanie typowi
.catch((e) => ...). Jegoetoany. Oznacz go jakounknown. - Używanie
new Promisewokół czegoś, co już zwraca promise. Po prostu zwróć istniejący promise albo użyj na nimawait.
Najczęściej zadawane pytania
Czym jest Promise<T> w TypeScript?
Promise<T> to typ promise'a, który rozwiązuje się wartością typu T. Funkcja zwracająca Promise<string> oddaje promise, którego callback then albo await daje string. Promise rozwiązujący się bez wartości to Promise<void>.
Jak otypować new Promise w TypeScript?
Przekaż argument typu: new Promise<number>((resolve, reject) => ...). Bez niego TypeScript nie wywnioskuje wartości z wywołań resolve i wynikiem jest Promise<unknown>. Dla promise'a, który rozwiązuje się bez wartości, użyj new Promise<void>(resolve => ...), żeby resolve() bez argumentu było dozwolone.
Jakiego typu jest błąd w catch promise'a?
any. Parametr reason callbacku catch ma w bibliotece standardowej typ any, bo rzucić lub odrzucić można cokolwiek. Sam oznacz go jako unknown (.catch((e: unknown) => ...)) i zawęź przez instanceof Error przed użyciem.
Jak Promise.all działa z typami w TypeScript?
Promise.all na literale tablicy zwraca typ krotki z jednym elementem na każde wejście, w kolejności: Promise.all([getUser(), getCount()]) to Promise<[User, number]>, więc destrukturyzacja daje każdej wartości jej własny typ. Na tablicy jednego typu, T[], zwraca Promise<T[]>.
Czym różni się Promise.all od Promise.allSettled?
Promise.all odrzuca, gdy tylko którekolwiek wejście zostanie odrzucone. Promise.allSettled zawsze się rozwiązuje, tablicą obiektów PromiseSettledResult<T>: { status: "fulfilled", value } albo { status: "rejected", reason }. Sprawdź status, żeby zawęzić każdy z nich.