Rozwiązanie z komentarzem
Opublikowano 21 sierpnia 2026Rozwiązanie
type Unique< T extends readonly unknown[], Acc extends unknown[] = [], > = T extends readonly [infer H, ...infer R] ? Includes<Acc, H> extends true ? Unique<R, Acc> : Unique<R, [...Acc, H]> : Acc
Najczęstsza błędna odpowiedź
type Unique<T extends readonly unknown[]> = T extends readonly [infer H, ...infer R] ? Includes<R, H> extends true ? Unique<R> : [H, ...Unique<R>] : []
To patrzy w przód, zamiast pamiętać. Gdy element powtarza się dalej, odrzuca *pierwsze* wystąpienie i zachowuje ostatnie, więc `[1, 2, 2, 3, 1]` daje `[2, 3, 1]`. Elementy są dobre, kolejność nie.
Linia po linii
Acc extends unknown[] = []Parametr typowy z wartością domyślną to sposób, w jaki funkcja na typach niesie stan. Wywołujący nigdy go nie podaje; każde wywołanie rekurencyjne przekazuje rosnący wynik dalej.
Includes<Acc, H>Test przynależności działa na tym, co już zachowano, a nie na tym, co dopiero nadejdzie. To właśnie różnica między zachowaniem pierwszego a ostatniego wystąpienia.
: AccGdy wejście się wyczerpie, akumulator *jest* odpowiedzią, więc zwracamy go wprost, zamiast składać wynik przy wychodzeniu. To kształt rekurencji ogonowej, a TypeScript obsługuje go mniejszym stosem niż formę głowa-ogon.
Do zapamiętania
Parametr akumulatora zamienia rekurencję budującą wstecz na budującą wprzód. Kosztuje jeden parametr typowy, a daje poprawną kolejność i głębszy limit rekurencji.
