Historia pytania
Domyślnie TypeScript dopuszcza pewną "luźność" w porównywaniu sygnatur typów funkcji, zezwalając, aby funkcje odwrotne i nieodwracalne były uznawane za zgodne. Od wersji TypeScript 2.6 wprowadzono opcję strictFunctionTypes, która zapewnia ścisłą kontrolę typów funkcji i zapobiega wielu klasom błędów, szczególnie w dużych bazach kodu.
Problem
Bez ścisłej kontroli istnieje możliwość, że funkcja obsługi lub callback akceptuje więcej lub bardziej specyficzny typ parametrów, co pozostaje niezauważone przez programistę. Prowadzi to do błędów runtime związanych z kowariancją typów zwracanych i kontrawariancją argumentów.
Rozwiązanie
Opcja strictFunctionTypes wprowadza ścisłą kontrawariancję dla typów parametrów funkcji. Teraz funkcje są zgodne tylko wtedy, gdy parametr typu źródłowego jest supertypem parametru docelowego, a nie odwrotnie.
Przykład kodu:
type Animal = { name: string }; type Cat = { name: string; meow: () => void }; let animalHandler: (a: Animal) => void; let catHandler: (c: Cat) => void; animalHandler = catHandler; // Błąd przy strictFunctionTypes: argument jest zbyt specyficzny catHandler = animalHandler; // Dozwolone, Cat — podtyp Animal
Kluczowe cechy:
Czy wcześniej można było przypisać handler z bardziej specyficznym typem parametru, zanim wprowadzono strictFunctionTypes?
Tak, przed włączeniem strictFunctionTypes TypeScript zezwalał na przypisanie bardziej specyficznych funkcji zamiast ogólnych, co prowadziło do problemów runtime:
enum E { A, B } const f: (e: E) => void = (e: E.A) => {} // Bez strictFunctionTypes: dozwolone
Jak strictFunctionTypes wpływa na callbacki z opcjonalnymi parametrami?
Jeśli parametry funkcji-callback czynią jakieś parametry opcjonalnymi, ścisła kontrola nie pozwoli na użycie funkcji z mniejszą liczbą obowiązkowych parametrów w miejscu, gdzie oczekuje się funkcji z większą liczbą. Zapobiega to sytuacji, w której callback nie otrzymuje potrzebnych danych.
Czy pojawią się problemy ze zgodnością przy włączeniu strictFunctionTypes w starych projektach?
Tak, istnieje ryzyko pojawienia się nowych błędów kompilacji, ponieważ wiele funkcji i handlerów mogło być przypisanych do siebie z naruszeniem kontrawariancji. Najczęściej zdarza się to przy wywołaniach zwrotnych lub przy używaniu API zewnętrznych bibliotek bez ścisłej typizacji.
W dużym projekcie handlerzy zdarzeń akceptują bardziej specyficzne typy (MouseEvent zamiast ogólnego Event). Nie jest to wykrywane do momentu włączenia ścisłej opcji, co prowadzi do błędów podczas uruchamiania z różnymi źródłami zdarzeń.
Zalety:
Wady:
Projekt korzysta z strictFunctionTypes od samego początku. Przy dodawaniu nowych handlerów wszystkie rozbieżności między typami są automatycznie wykrywane przez kompilator. Kod staje się bardziej odporny na literówki, łatwiejszy w utrzymaniu.
Zalety:
Wady: