Exclude<T, U> — to typ pomocniczy, który pojawił się w TypeScript w celu odjęcia jednego typu od drugiego, gdy wymagane jest wykluczenie niektórych wartości z typu union.
Początkowo w TypeScript nie było wygodnego sposobu na odjęcie typu od innego. Przy tworzeniu uogólnionych API, jak również podczas refaktoryzacji często wymagano uzyskania "resztowego" typu — wszystkiego oprócz zabronionych wartości. Zamiast ręcznych manipulacji z union, trzeba było utrzymywać kilka podobnych interfejsów.
Na przykład, gdy mamy typ 'A | B | C', ale potrzebujemy uzyskać typ bez B. Jest to często wymagane przy budowaniu skomplikowanych parametrów wejściowych funkcji, filtrowaniu dozwolonych wartości i dynamicznym formowaniu typów.
Exclude rozwiązuje ten problem. Jego uproszczona sygnatura jest taka:
type Exclude<T, U> = T extends U ? never : T;
Zwraca typ, który wyklucza z T wszystkie człony U.
Przykład:
type Status = 'draft' | 'published' | 'removed'; type UserVisibleStatus = Exclude<Status, 'removed'>; const visible: UserVisibleStatus = 'draft'; // OK
Kluczowe cechy:
Czy można używać Exclude dla typów zwykłych, a nie union?
Jeśli T nie jest typem union, ale wchodzi w skład U — Exclude i tak zadziała, ale wynikiem może być never lub T, co nie zawsze jest intuicyjne.
Exclude<'a', 'a'> // wynik: never Exclude<'a', 'b'> // wynik: 'a'
Czy Exclude usuwa wszystkie odniesienia do typu w strukturze obiektu?
Nie, Exclude nie przechodzi rekurzywnie po zagnieżdżonych polach typu, wyklucza tylko na najwyższym poziomie union.
Jak działa Exclude z interfejsami i typami-objektami?
Porównuje cały typ, a nie poszczególne właściwości. Dlatego Exclude z union kilku interfejsów — usuwa tylko te, które całkowicie pokrywają się z U.
interface A { x: number }; interface B { y: string }; // Exclude<A|B, B> daje: A (B całkowicie się pokrywa)
Walidacja ról użytkownika przez Exclude<UserRoles, 'admin'>, ale zapomnieliśmy, że Exclude nie ma zastosowania do zagnieżdżonych struktur — uprawnienia 'admin:sub' nie zostały wykluczone.
Zalety:
Wady:
Użycie Exclude do ograniczenia publicznego API operacjami: Exclude<Action, 'delete'>, co wyklucza niebezpieczną operację.
Zalety:
Wady: