{
    "slug": "typescript_variance",
    "term": "Covariance & Contravariance",
    "category": "typescript",
    "difficulty": "advanced",
    "short": "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.",
    "long": "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).",
    "aliases": [
        "covariance",
        "contravariance",
        "bivariance",
        "variance annotations",
        "in out type parameters"
    ],
    "tags": [
        "typescript",
        "types",
        "variance",
        "generics",
        "advanced-types"
    ],
    "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."
    ],
    "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."
    ],
    "avoid_when": [
        "Variance annotations are unnecessary for simple non-generic interfaces."
    ],
    "related": [
        "typescript_generics",
        "typescript_types",
        "typescript_conditional_types"
    ],
    "prerequisites": [
        "typescript_generics",
        "typescript_interfaces_types"
    ],
    "refs": [
        "https://www.typescriptlang.org/docs/handbook/2/generics.html#variance-annotations"
    ],
    "bad_code": "// Method shorthand — bivariant (unsound)\ninterface Processor {\n    process(input: Dog): void; // bivariant — accepts Dog or Animal\n}\n\n// Mutable covariant array — unsafe\nconst dogs: Dog[] = [new Dog()];\nconst animals: Animal[] = dogs;  // OK in TS\nanimals.push(new Cat());          // Runtime: dogs now contains a Cat!",
    "good_code": "// Function property — strictly contravariant (safe)\ninterface Processor {\n    process: (input: Dog) => void;\n}\n\n// TypeScript 4.7 explicit variance\ninterface Producer<out T> { get(): T }     // covariant — only produces T\ninterface Consumer<in T> { set(v: T): void } // contravariant — only consumes T\ninterface Invariant<in out T> { transform(v: T): T } // invariant",
    "example_note": "Shows the difference between bivariant method shorthand and contravariant function properties, plus TypeScript 4.7 explicit in/out variance annotations.",
    "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.",
    "severity": "low",
    "effort": "high",
    "created": "2026-04-11",
    "updated": "2026-04-11",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/typescript_variance",
        "html_url": "https://codeclaritylab.com/glossary/typescript_variance",
        "json_url": "https://codeclaritylab.com/glossary/typescript_variance.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[Covariance & Contravariance](https://codeclaritylab.com/glossary/typescript_variance) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/typescript_variance"
            }
        }
    }
}