Covariance & Contravariance
Also Known As
covariance
contravariance
bivariance
variance annotations
in out type parameters
TL;DR
Variance describes how subtype relationships on a type's components affect subtype relationships on the whole type — covariant types follow the direction, contravariant types reverse it.
Explanation
Covariance: if Dog extends Animal, then Dog[] extends Animal[] — arrays are covariant in their element type. Producer types (return positions) are covariant. Contravariance: a function that accepts Animal is assignable to one that accepts Dog — function parameters are contravariant (you can pass a more general handler). TypeScript is structurally typed so variance is checked structurally. Method shorthand syntax (method(): void) is bivariant (both directions) for historical reasons — function property syntax (method: () => void) is strictly contravariant, which is safer. TypeScript 4.7 added explicit variance annotations: in T (contravariant), out T (covariant), in out T (invariant).
Diagram
flowchart LR
subgraph Covariant
DA[Dog extends Animal]
DA --> DA2[Dog array extends Animal array]
end
subgraph Contravariant
FN[fn Animal-void]
FN --> FN2[assignable to fn Dog-void<br/>reverses direction]
end
subgraph Invariant
INV[ReadWrite T<br/>neither sub nor super]
end
Watch Out
⚠ TypeScript's structural typing means variance isn't always enforced as strictly as in nominal type systems like Java. The --strictFunctionTypes flag (included in --strict) enables contravariance for function types.
Common Misconception
✗ Method parameters in TypeScript are always strictly contravariant — method shorthand syntax (m(x: T)) is actually bivariant due to a historical unsoundness. Use function property syntax (m: (x: T) => void) for strict contravariance.
Why It Matters
Misunderstanding variance leads to subtle runtime type errors that TypeScript doesn't catch — particularly when assigning arrays of subtypes to mutable containers or using method shorthand.
Common Mistakes
- Using method shorthand instead of function property syntax — loses contravariance checking on parameters.
- Mutating a covariant array — Dog[] assignable to Animal[] seems fine until you push a Cat into it via the Animal[] reference.
- Expecting a callback accepting a subtype to be assignable to one accepting a supertype — it's actually the reverse (contravariance).
- Not using in/out variance annotations on generic interfaces where correctness matters.
Avoid When
- Variance annotations are unnecessary for simple non-generic interfaces.
When To Use
- Designing generic interfaces that are clearly producers (out T) or consumers (in T) for safer assignments.
- Library authors ensuring their generic types compose correctly with user-defined subtypes.
Code Examples
💡 Note
Shows the difference between bivariant method shorthand and contravariant function properties, plus TypeScript 4.7 explicit in/out variance annotations.
✗ Vulnerable
// Method shorthand — bivariant (unsound)
interface Processor {
process(input: Dog): void; // bivariant — accepts Dog or Animal
}
// Mutable covariant array — unsafe
const dogs: Dog[] = [new Dog()];
const animals: Animal[] = dogs; // OK in TS
animals.push(new Cat()); // Runtime: dogs now contains a Cat!
✓ Fixed
// Function property — strictly contravariant (safe)
interface Processor {
process: (input: Dog) => void;
}
// TypeScript 4.7 explicit variance
interface Producer<out T> { get(): T } // covariant — only produces T
interface Consumer<in T> { set(v: T): void } // contravariant — only consumes T
interface Invariant<in out T> { transform(v: T): T } // invariant
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
11 Apr 2026
Views
37
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
No pings yesterday
Perplexity 3
Google 2
Unknown AI 2
ChatGPT 1
SEMrush 1
Ahrefs 1
How they use it
crawler 10
Related categories
⚡
DEV INTEL
Tools & Severity
🟢 Low
⚙ Fix effort: High
⚡ Quick Fix
Enable --strict (includes strictFunctionTypes). Prefer function property syntax over method shorthand. Use 'in'/'out' variance annotations on generic interfaces where producer/consumer roles are clear.
📦 Applies To
typescript 4.7
web
cli
🔗 Prerequisites
🔍 Detection Hints
\w+\(\w+:
Auto-detectable:
✗ No
typescript
🤖 AI Agent
Confidence: Low
False Positives: High
✗ Manual fix
Fix: High
Context: File