Given Dog is a subtype of Animal, variance describes how a generic G<T> treats that relation:
- Covariant (
out T):G<Dog>is assignable toG<Animal>.Tis only produced: return types, readonly properties,ReadonlyArray<T>. - Contravariant (
in T):G<Animal>is assignable toG<Dog>.Tis only consumed: function parameters. A function accepting anyAnimalis safe wherever a function acceptingDogis expected, not the reverse. - Invariant (
in out T): neither direction.Tis both read and written, like a mutable box.
strictFunctionTypes (TS 2.6, in strict) checks function parameters contravariantly. Without it they were bivariant (either direction passed). The PR excludes anything declared with method syntax, so Array<T> and most interfaces stay covariant: interface H { handle(x: Dog): void } is still bivariant, while handle: (x: Dog) => void is checked strictly. This is a deliberate unsoundness; dogs assigned to Animal[] then push of a non-Dog compiles.
in / out annotations (TS 4.7) are written on type parameters of classes, interfaces and aliases. TypeScript normally measures variance itself by relating instantiations structurally; the annotation (a) documents intent, (b) is checked, producing TS2636 when the body contradicts it, and (c) lets the compiler compare type arguments directly instead of walking the structure, which the 4.7 notes say is faster for complex or recursive types.
An annotation overrides the method exemption: Box<in out T> with method-syntax get() and set(v) is invariant (neither direction assigns, checked), while the same members without annotations let Plain (with Dog) assign to PlainA (with Animal). The reverse also holds: out T on an interface whose set(v: T) is a method does not error (checked), because method parameters are bivariant, so the annotation cannot catch that unsoundness.