← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Covariance & Contravariance

TypeScript 4.7 Advanced
debt(d7/e5/b5/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). The detection_hints specify automated=no and the only tool listed is TypeScript itself. Even with --strict/strictFunctionTypes enabled, the bivariance hole in method shorthand syntax is not caught — it requires careful code review to notice the difference between method shorthand and function property syntax, and mutable covariant array misuse is silent until runtime.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix mentions enabling --strict and converting method shorthand to function property syntax throughout interfaces, plus adding in/out variance annotations where needed. This is not a single-line fix — it requires auditing all generic interfaces and method signatures, potentially touching many files across a codebase.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). Variance applies to web and cli contexts broadly. Every generic interface and callback-accepting API is affected by this concern. Library authors and any developer designing generic types must keep variance rules in mind continuously, but it doesn't fully define the system's shape — it's a persistent tax on type-system work rather than an architectural constraint.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap (contradicts how a similar concept works elsewhere)' (t7). The misconception field explicitly states that method shorthand syntax (m(x: T)) is bivariant — contradicting the expectation that TypeScript would enforce contravariance consistently. Developers familiar with type theory or other typed languages expect method parameters to behave contravariantly, but TypeScript's method shorthand silently allows unsound assignments. The covariant array mutation issue is an additional well-known but non-obvious trap.

About DEBT scoring →

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

Added 11 Apr 2026
Views 65
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 1 ping M 1 ping T 0 pings W 0 pings T 0 pings F 1 ping S 1 ping S 1 ping M 0 pings T 0 pings W 1 ping T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 1 ping W 1 ping T 0 pings F 1 ping S 1 ping S 0 pings M 1 ping T 0 pings W
No pings yet today
PetalBot 1
SEMrush 5 Google 4 Perplexity 3 Ahrefs 3 Scrapy 3 Unknown AI 2 Claude 2 Bing 2 Meta AI 2 PetalBot 2 ChatGPT 1 Sogou 1
crawler 28 crawler_json 2
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


✓ schema.org compliant