Zagadnienie 7 z 12
infer
`infer` to dopasowanie wzorca. Opisujesz oczekiwany kształt, wstawiasz `infer X` tam, gdzie siedzi interesujący fragment, a kompilator rozwiązuje `X`. Większość pomocników wyciągających z biblioteki standardowej to jeden warunek z jednym `infer` we właściwym miejscu.
Działa wszędzie, gdzie może pojawić się typ
type Unwrap<T> = T extends Promise<infer Inner> ? Inner : T type Element<T> = T extends (infer E)[] ? E : never type Returns<T> = T extends (...a: never[]) => infer R ? R : never type Head<T> = T extends [infer H, ...unknown[]] ? H : never type Rest<S> = S extends `${string}-${infer R}` ? R : never
Obietnice, tablice, zwracane typy funkcji, pozycje w krotce, wzorce łańcuchów — mechanizm jest za każdym razem ten sam. Zmienia się tylko otaczający kształt — dlatego nauka jednego uczy wszystkich.
Ograniczanie tego, co zostaje związane
// Without the constraint, F is unknown and cannot be // interpolated into a template literal. type First<T> = T extends [infer F extends string, ...unknown[]] ? `${F}!` : never
`infer` może nieść własną klauzulę `extends`, która zawęża wiązanie i pozwala użyć wyniku tam, gdzie wymagany jest konkretny rodzaj typu. Bez niej często dostajesz `unknown`, a błąd pojawia się linijkę po przyczynie.
Najczęstsza błędna odpowiedź
// Intent: get the return type of any function. type Returns<T> = T extends () => infer R ? R : never Returns<() => string> // string Returns<(a: number) => string> // never — pattern demands zero parameters // Fixed: never[] in parameter position accepts any signature, // because parameters are checked contravariantly. type Returns2<T> = T extends (...a: never[]) => infer R ? R : never
Wzorzec musi być tak pobłażliwy jak wejścia, które chcesz dopasować. Typ funkcji bez parametrów pasuje tylko do funkcji bez parametrów, więc wszystko inne po cichu wpada w gałąź fałszywą.
Do zapamiętania
Opisz kształt, zaznacz dziurę przez `infer` i pozwól kompilatorowi ją wypełnić. Jeśli nic nie pasuje, to wzorzec jest surowszy niż wejście — nie odwrotnie.