Historia pytania
Wraz z rozwojem TypeScript pojawiła się potrzeba automatycznego wyodrębniania typu wartości zwracanej z funkcji, zwłaszcza w dużych projektach z wieloma powiązanymi ze sobą funkcjami. W tym celu wprowadzono typ pomocniczy ReturnType<T>, który został wprowadzony w standardowej bibliotece od wersji TypeScript 2.8.
Problem
W dużych i złożonych projektach może być trudno utrzymać aktualność typów — jeśli ręcznie określa się typ wartości zwracanej każdej funkcji i jednocześnie wprowadza zmiany w sygnaturach, łatwo może pojawić się niespójność, gdy sygnatury i implementacje nie pasują do siebie. Dodatkowo, jeśli funkcja zwraca złożoną strukturę, nie zawsze łatwo jest ręcznie zsynchronizować opis zwracanego typu we wszystkich miejscach użycia.
Rozwiązanie
Typ pomocniczy ReturnType<T> automatycznie wyodrębnia typ zwracany przez funkcje i może być używany do typizacji wyniku wywołania funkcji w dowolnym miejscu kodu. To zmniejsza ilość ręcznego wsparcia infrastruktury typów i minimalizuje błędy związane z niespójnością opisanego i rzeczywistego typu wartości zwracanej.
Przykład kodu:
function createUser(name: string, age: number) { return { name, age, created: new Date() }; } type User = ReturnType<typeof createUser>; // User: { name: string; age: number; created: Date; }
Kluczowe cechy:
Czy można używać ReturnType z metodami klas?
Tak, ale ważne jest, aby pamiętać o kontekście: jeśli metoda jest właściwością obiektu z funkcją, należy użyć ReturnType<obj['metoda']>.
Przykład kodu:
class MyClass { foo(x: number) { return x * 2; } } type FooReturn = ReturnType<MyClass['foo']>; // Błąd typu! // Należy tak: type FooReturn = ReturnType<(x: number) => number>; // number // Lub wyodrębnić metodę jako funkcję: const obj = new MyClass(); type FooReturn2 = ReturnType<typeof obj.foo>;
Co zwróci ReturnType dla funkcji z void/never?
Dla funkcji o zadeklarowanym typie void, ReturnType zwróci void. Dla never — never.
Przykład kodu:
function doNothing(): void {} type Result = ReturnType<typeof doNothing>; // void
Czy ReturnType działa z przeciążeniami funkcji?
Nie, ReturnType wyodrębnia zwracany typ samej "implementacji", a nie wszystkich przeciżeń. Jeśli jest kilka przeciżeń — brany jest opisany typ implementacji.
Przykład kodu:
function func(x: number): number; function func(x: string): string; function func(x: any): any { return x } type RT = ReturnType<typeof func>; // any
W projekcie zadeklarowano ręczny typ dla zwracanego obiektu funkcji. Funkcja się zmienia — typ nie jest aktualizowany, wywołania padają w czasie wykonywania.
Zalety:
Wady:
Przechodzą na ReturnType wszędzie tam, gdzie używają wartości zwracanej przez funkcję. W przypadku zmian typ jest zawsze aktualny.
Zalety:
Wady: