Zaimplementuj `MyExclude<T, U>`, aby usuwał z unii `T` każdy element przypisywalny do `U`. To jedna linia — subtelność polega na tym, dlaczego ta linia w ogóle działa.
01
Spróbuj rozwiązać samodzielnie
Par 3
Zagadka
rebuild-exclude.ts
type MyExclude<T, U> = ???
Podejście 1 z 3Jeszcze nie sprawdzone
Zastąp ??? — rozwiązanie sprawdzamy przypadkami poniżej. Tab wcina; naciśnij Escape, a potem Tab, by wyjść fokusem.
Testy
4
MyExclude<'a' | 'b' | 'c', 'a'>—
→ 'b' | 'c'
MyExclude<string | number, number>—
→ string
MyExclude<'a', 'a'>—
→ never
MyExclude<'a' | 'b', 'c'>—
→ 'a' | 'b'
Jak oceniamy test Dokładna równość typów, nie przypisywalność — przecięcie to nie to samo co spłaszczony obiekt.
Jak poszło innym
Tę zagadkę rozwiązało jak dotąd mniej niż 5 osób. Rozkład pojawi się, gdy próbka będzie na tyle duża, by coś znaczyć.
type MyExclude<T, U> = [T] extends [U] ? never : T
Owinięcie `T` w krotkę wyłącza dystrybucję, więc cała unia porównywana jest naraz. `["a" | "b" | "c"] extends ["a"]` jest fałszem i cała unia wraca nieprzefiltrowana.
Linia po linii
T extends U
`T` jest tu *nagie* — stoi samo po sprawdzanej stronie, nie owinięte w krotkę, tablicę ani obiekt. To dokładnie ten warunek uruchamia dystrybucję po unii.
? never : T
Każdy element albo staje się `never`, albo przeżywa jako on sam. Złączenie wyników automatycznie usuwa `never`, bo `never` jest elementem neutralnym unii.
Do zapamiętania
Dystrybucja to różnica między pytaniem o unię a pytaniem o każdy jej element. `[T] extends [U]` to przełącznik, który ją wyłącza.