Skip to content

TypeScript doesn't consider contravariance in member functions even with strictFunctionTypes #63566

@alecov

Description

@alecov

🔎 Search Terms

contravariance strictFunctionTypes function method

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about strictFunctionTypes and the distinction between function and method properties during typechecking.

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=6.0.0-dev.20260416#code/C4TwDgpgBAogHgQwLZgDYQDwBUoF4oDOwATgJYB2A5lAD5TkCuSARhMQHx5QDeAsAFBQoAemFQAshGAALAPYATQiHLAEcAUKRS58gBRwAXFCwBKIwDdZpeQG4NIsQDEG5AMbBSs8kpVr7AMxdXI30jUzxOS2s7fgBfGIFAtw8vKH8AJlDYRBR0DCIyKnYTHns4ADotGQUuLIKKShLcTlcvAll0ctRZSn0TGKEKpNdaw0ISBqaWto6ILp6+mNiBAVEoAHViL0pUECgEV1cIMGACA1WxAAMhoMuoVwRvL12oYggj0nNoeqoCABooMwGMAoDJoGAEAQCBBFAhiJQmBAVKDZGl0vtUN0AO4EC70JisYgEFGA6AMaGKLHSJH3BCYhpQCA5NAQdLlYa6MwCVrkIiM5nodH4bixfbE+DIFn5CZUWj4lhsdgxDK6JmSwX9ARq3Ks9lBXQARgADJr+HisKj-KQ4KDpKQzvt5Ip5EwkHstAqiaDwDCxbboMMUt4IcRkFI2N7IGcLnjQJBsurMDh8D9qHRGJ7OMK8UIoAB9PNwyhYH1hGJrXPlKt42JAA

💻 Code

type Example<T = string | number> = {
  // Function syntax
  func: (x: T) => void;
};

function f2(x: Example<string>) {
  x.func = (x: string) => console.log(x);
}

// Wrongly accepts:
// `x.func` can only receive strings, but the passed argument to f2 allows
// numbers to be used when calling example2.func():
const example2 = {} as Example<string | number>;
f2(example2);
example2.func(10); // will pass a number instead of a string which is now expected by the callback assigned by f2

🙁 Actual behavior

The call f2(example2) does not produce any errors. See below to why I believe this is wrong.

🙂 Expected behavior

The call f2(example2) should error on the fact that Example<string | number> cannot fit Example<string> because func -- which here is typed as a function property, not a method property -- is contravariant on its argument.

I emphasize that this already takes into consideration the usual distinction between "method syntax" vs. "function syntax" as specified in the FAQ and several other related bug reports, such as #53798.

This comment: #53798 (comment) suggest strictFunctionTypes would apply to the situation here described, so I wonder if this is a bug or actually a design limitation of some sort.

Additional information about the issue

Adding a dummy member such as:

type Example<T = string | number> = {
   __argType: T;
   /*...*/
}

is an (ugly) workaround that mostly makes the type checker work as intended.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions