De ce acest fragment permite TypeScript să deducă un generic dintr-o funcție „părinte”?
Se pare că există o funcționalitate nedocumentată care permite funcțiilor să deducă un tip dintr-o funcție în care sunt transmise ca argument.
Încerc să înțeleg de ce funcționează asta și dacă este intenționat.
Teren de joacă
Dat un Model, atunci cu o funcție generică update și apply, pot deduce un tip din apply atunci când folosesc rezultatul lui update ca argument cu trucul T extinde infer U ? U : never:
interface Model {
foo: number
bar: string
}
function apply<T>(fn: (t: T) => T) {};
function whyDoesThisWork() {
function update<T>(u: T extends infer U ? Partial<U> : never) { return (t: T) => ({ ...t, ...u }) };
// Somehow, Typescript is able to infer that we want Model to be the generic supplied for update
apply<Model>(update({ foo: 1 }));
}
function thisDoesntWorkForObviousReasons() {
function update<T>(u: Partial<T>) { return (t: T) => ({ ...t, ...u }) }
// update infers T as { foo: 1 }, because that makes sense
apply<Model>(update({ foo: 1 }));
}
Răspuns #1
Acest comportament apare din cauza tipizării contextuale.
„Trucul” poate fi extras într-un tip ajutător numit DontInfer:
type DontInfer<T> = T extends infer S ? S : never;
Când DontInfer este utilizat într-o semnătură de funcție generică, T nu va fi dedus acolo unde este folosit. Din exemplul meu, argumentul generic u poate fi considerat de forma Partial<T>, dar T nu va fi dedus de u:
function update<T>(u: DontInfer<Partial<T>>) { return (t: T) => ({ ...t, ...(u as Partial<T>) }) };
Acest lucru înseamnă că funcția update nu va putea să deducă T dacă este folosită de una singură:
// T is inferred to be `unknown`
update({ foo: 1 })
Dar atunci când este folosit într-un context, T poate fi dedus:
// T is inferred to be Model
setState<Model>(update({ foo: 1 }));
DontInfer funcționează în acest scop deoarece amână evaluarea tipurilor condiționale nerezolvate.
