Skip to content

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 as not planned
@edrennikov

Description

@edrennikov

🔎 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions