Variance annotations in TypeScript (like out
and in
) declare how type parameters behave in subtyping relationships:
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
, thenEffect<Cat>
is assignable toEffect<Animal>
- Used for "producers" of values
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
The type parameter appears in both positions
- No automatic subtyping relationship
- Must be exact type match
out A, out E, out R
means:
- You can assign
Effect<string, Error>
toEffect<string | number, Error>
- You can assign
Effect<A, SpecificError>
toEffect<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.