[[Variance (Covariance, Contravariance, Invariance)|Variance (Covariance, Contravariance, Invariance)]]
📌 Brief Summary
Variance describes how the subtyping relationship between complex types (such as functions or generics) relates to the subtyping relationship of their component types. In TypeScript's type system, understanding variance is critical for designing safe interfaces, as it determines whether a subtype can be substituted for a supertype without violating type safety.

📖 Core Content
In the context of TypeScript interface design, variance defines the rules for "substitutability" (Liskov Substitution Principle) when dealing with generic parameters or function signatures.

* **Covariance (Preserving Direction):**
  A type relationship is covariant if it preserves the ordering of types. If $A$ is a subtype of $B$ ($A <: B$), then $F(A)$ is a subtype of $F(B)$. 
  - **In TypeScript:** This is primarily seen in "producer" positions. For example, an array of `Dog` (`Dog[]`) is a subtype of `Animal[]` because the elements being read out are guaranteed to be at least `Animal`. In function types, the **return type** is covariant. If a function returns a `Dog`, it can safely be used where a function returning an `Animal` is expected.

* **Contravariance (Reversing Direction):**
  A type relationship is contravariant if it reverses the ordering of types. If $A <: B$, then $F(B)$ is a subtype of $F(A)$.
  - **In TypeScript:** This occurs in "consumer" positions. The most critical application is in **function arguments**. If a function expects an argument of type `Animal`, you can safely pass it a function that accepts `Object` (because a function capable of handling any `Object` can certainly handle a `Dog`). However, you cannot pass a function that specifically requires a `Dog` to a caller expecting an `Animal` handler, as this would violate type safety when a `Cat` is passed.

* **Invariance (No Relationship):**
  A type is invariant if $F(A)$ is only a subtype of $F(B)$ if $A$ and $B$ are identical. There is no subtyping relationship allowed between the generic containers, regardless of the relationship between the underlying types.
  - **In TypeScript:** This occurs when a type acts as both a producer and a consumer (e.g., a mutable property in an interface). If you have a property `value: T`, it must be invariant. If it were covariant, you could write a `Cat` into an `Animal` array; if it were contravariant, you could read a `Cat` from an `Object` reference. To prevent runtime errors during mutation, TypeScript enforces invariance on mutable generic members.

* **Application in Interface Design:**
  When designing interfaces, developers must decide the variance of generic parameters. Using `in` (contravariant) and `out` (covariant) annotations (though primarily a feature of languages like C# or Scala, the logic applies to how TypeScript infers compatibility) allows for more flexible and reusable API designs.

🔗 Knowledge Connections
* Related Topics: [[Liskov-Substitution-Principle|Liskov Substitution Principle]], Function Type Compatibility
* Projects/Contexts: TypeScript Structural Type System
* Contradictions/Notes: Note that TypeScript's type system is "structural," meaning variance is determined by the shape of the object rather than explicit declarations, which can sometimes lead to unexpected compatibility in complex nested generics.

Last updated: 2026-04-17