TypeScript theorytheory 0/50 · 0%
Types · hard

41. Variance and function type compatibility

Why some function types are assignable to others, and some aren't.

A function type `A` is assignable to another function type `B` if `A`'s parameters are compatible with `B`'s (contravariantly, loosely enforced for methods) and `A`'s return type is assignable to `B`'s return type (covariantly).

ts
type Handler = (event: { type: string; amount: number }) => void;

// A function that accepts fewer/broader-typed properties can be assigned,
// since it can handle anything Handler's caller passes it:
const logAmount: (event: { amount: number }) => void = (e) => console.log(e.amount);
const h: Handler = logAmount; // OK: logAmount only needs 'amount', which Handler always provides

function process(items: number[], cb: (n: number) => void) {
  items.forEach(cb);
}
process([1, 2, 3], (n: number) => console.log(n));

Return types must be compatible in the same direction as the assignment (a function returning a more specific type can substitute for one expecting a more general type), while `strictFunctionTypes` tightens parameter checking for standalone function types (not methods) to catch genuinely unsound assignments.

Check your understanding

  1. 1. Can a function needing fewer properties (e.g. `{ amount: number }`) be assigned where a handler expecting `{ type: string; amount: number }` is required?

  2. 2. In which direction must a function's return type be compatible for assignability?

  3. 3. What does `strictFunctionTypes` primarily tighten?