infer introduces a type variable in the extends clause of a conditional type. The compiler solves it by matching the checked type against the pattern, the same way it infers type arguments at a call; if the match fails, you get the false branch. The variable is in scope only in the true branch, and infer is a syntax error anywhere else (TS1338).
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T; // simplified
ReturnType matches "a function returning something" and names that something R. The lib version of Awaited (TS 4.5) is more careful: it only unwraps object types with a callable then, and special-cases null | undefined, mirroring what await does at runtime.
Rules worth knowing:
- Positions work anywhere a type can be matched: return slot, parameter list (
infer Pyields a tuple), tuple head/tail ([infer H, ...infer T]), array element, object property, template literal segments. - Several candidates. Same
infer Uin covariant positions (properties, returns) gives a union; in contravariant positions (function parameters) gives an intersection. - Overloads. Inference uses the last signature. Generic signatures are inferred with their parameters as
unknown. infer X extends C(TS 4.7) constrains the inferred variable and falls to the false branch if it does not fit, replacing a nested conditional. From 4.8, in template literals it also parses literals:"42"infers42.- Distribution still applies to a naked checked type, so
inferover a union maps member by member.