Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compile does not produce an error when parameters in overriding class method in child class have narrower type than the parameters of this method in the parent class #58122

Closed
edrennikov opened this issue Apr 8, 2024 · 4 comments

Comments

@edrennikov
Copy link

edrennikov commented Apr 8, 2024

🔎 Search Terms

[class ](is:issue is:open label:Bug inherit)

🕗 Version & Regression Information

  • This is a crash
  • This changed between versions ______ and _______
  • This changed in commit or PR _______
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about strictFunctionTypes here
  • I was unable to test this on prior versions because _______

⏯ Playground Link

https://www.typescriptlang.org/play?#code/PTAEBUAsFNQYwPYBNYCNoBsEHdQEsBneBAWwAc8NolRs8AXSBAV3tGgCcOEP2A3aADtaDSKAC04gvQ5449AGLNB8vAkHgAnmWgEAUCFABDDIxYBzMQ1BluSZnF3Hhnbr2yRKaaHkHn2AB7QcKzUenq+9JwAZkaOoACSoADeeqDpxgBcoILMJOgcANx6AL7hkTFxsABSgVGCSERJqRmgqNnSsn7FZXpwGEYERACCKWkZZMyoGHKgJAAUAdkJAJTZufmcY62tHND0zBzCAQB0RsWtJT3h-YNEAEJ1Qo2go6nj6YZQhKAIAlx4FBEEj7Jg0ABEJHB+GEtyGoHBw3BJw+oEMAGUHGI-q5AbASMxpG1YNEeKhAShBAAaVGGdBwIyE2DWQRGLg4IhIHDCRiwMhsowgqK8ejaWAIaJzUHIBFQ4jkNnUUD0BA2RWCNhwgg01qGDxyMQMplEIwudnuGDCBCoABWwTYEvgA3h4Pu0J+TJovmk0CMNEd1rt8l+kq1CKRKNak2mswWS1A1TWOTyBW2O3SewOR1ApyMoAA1DmTqgTlQ-IwLhkrqVwtFlKp1KBootssMkxtUy0MohBESyMtQABeFJGbIARmru32h2OJwWZBW1z66iJ7VAj2HgmguHu80X4UMShU9DUwnB0WhcUcZHoJo45jyQgdoedRFdl4wBFV9MZBFg7-wE1QAIKYw0dRFwRpOlWHwNhbGQBwnHBABhU1BAQNg9j9GxuB0DgTycR1lBQaJfCVeYsKQXx-AAcjLcxGBolZwWXXs2HjDstmHZtUEXIA

💻 Code

// The code below is compiled without error even with --strictFunctionTypes
// although it produces an error while being executed

interface I {
    a: number;
}

interface J extends I {
    b: string;
}

class A {
    public m(x: I): number {
        return x.a;
    };
}

class B extends A {

    // This overrides method "m" in class "A".
    // Such override must be forbidden,
    // because it narrows down the parameter type of method "m" compared to parent class,
    // which causes an error when object of class "B" is used instead of object of class "A".
    public m(x: J): number {
        return x.a + x.b.length;
    };
}

function f(x: A): number {
    const p: I = {a: 1};
    return x.m(p);
}

const b: B = new B();

// Function "f" accepts argument of class "B" also because "B" is a subclass of "A",
// but it produces "Cannot read properties of undefined (reading 'length')"
const x: number = f(b);

🙁 Actual behavior

The code above is compiled without an error even with --strictFunctionTypes, but when executed it gives Cannot read properties of undefined (reading 'length') error. Bug here is that TypeScript compiler does not recognize an error in the code above.

🙂 Expected behavior

I expect that code above should produce a compilation error (see additional information below).

Additional information about the issue

Below is the similar code, but if compiled with --strictFunctionTypes TypeScript gives an error, which is expected. The difference of the code below from the code above is that in the above code m is a method of classes, but in the code below m is a member of classes.

// The code below has compilation error with --strictFunctionTypes
// and has no compilation error without --strictFunctionTypes
// This is expected behaviour.
//
// Error witht --strictFunctionTypes:
// Property 'm' in type 'B' is not assignable to the same property in base type 'A'.
//   Type '(x: J) => number' is not assignable to type '(x: I) => number'.
//     Types of parameters 'x' and 'x' are incompatible.
//       Property 'b' is missing in type 'I' but required in type 'J'.

interface I {
    a: number;
}

interface J extends I {
    b: string;
}

class A {
    public m = (x: I) => {
        return x.a;
    };
}

class B extends A {
    public m = (x: J) => { // <-- here is a compilation error
        return x.a + x.b.length;
    };
}

I wonder why --strictFunctionTypes enables contravariance for function parameters, but not covariance? This doesn't seem logical, because as it was shown above it allows a runtime error which could be caught at compile time.

@MartinJohns
Copy link
Contributor

This is working as intended. There's even a section in the FAQ about this, and the behavior is required for some types (e.g. Arrays). Note that strictFunctionTypes applies to functions, not methods.

@edrennikov
Copy link
Author

But as provided in the example above there is obviously an error in the code which can be caught at compile time.

@MartinJohns
Copy link
Contributor

MartinJohns commented Apr 8, 2024

Yes, that's true. But it's still working as intended. TypeScripts type system is not fully sound (and it's not intended to).

Your intended change would break a whole lot of other stuff. See also #57783 for more information.

@edrennikov
Copy link
Author

TypeScripts type system is not fully sound (and it's not intended to)

That's sad 😕 Another broken dream in software development 🥲

@edrennikov edrennikov closed this as not planned Won't fix, can't repro, duplicate, stale Apr 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants