Skip to content

Instantly share code, notes, and snippets.

@hew
Last active October 2, 2025 11:31
Show Gist options
  • Save hew/3bf6d29509e9e8f246bd18faf3279ec6 to your computer and use it in GitHub Desktop.
Save hew/3bf6d29509e9e8f246bd18faf3279ec6 to your computer and use it in GitHub Desktop.
[reel] Variance Annotations in TS

Variance Annotations in TypeScript

Variance annotations in TypeScript (like out and in) declare how type parameters behave in subtyping relationships:

out (covariant)

The type parameter appears only in output/return positions

  • Effect<out A, out E, out R> means all three parameters are covariant
  • If Cat extends Animal, then Effect<Cat> is assignable to Effect<Animal>
  • Used for "producers" of values

in (contravariant)

The type parameter appears only in input/parameter positions

  • If Animal extends Cat, then (cat: Cat) => void is assignable to (animal: Animal) => void
  • Used for "consumers" of values

No annotation (invariant)

The type parameter appears in both positions

  • No automatic subtyping relationship
  • Must be exact type match

In Effect's case

out A, out E, out R means:

  • You can assign Effect<string, Error> to Effect<string | number, Error>
  • You can assign Effect<A, SpecificError> to Effect<A, Error> (if SpecificError extends Error)
  • This makes Effect types more flexible in subtyping scenarios while maintaining type safety

These annotations help TypeScript's type checker understand valid assignments and improve type inference performance by explicitly declaring the variance rather than having TypeScript infer it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment