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

The private symbol property leads a conflict with the implemented interface. #26206

Closed
fenying opened this issue Aug 4, 2018 · 3 comments
Closed
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@fenying
Copy link

fenying commented Aug 4, 2018

TypeScript Version: v3.1.0-dev.20180804

Search Terms: private symbol property this generic parameter

Code

type F<T> = (this: T) => void;

interface IClassA {

    hello(): F<this>;
}

const p1 = Symbol("class:a:p1");

class A
implements IClassA {

    private [p1]: string;

    public hello(): F<this> {

        return function(): void {

            this[p1] = "123";

            delete this[p1];

            return;
        };
    }
}

// Here reports an error saying "Property '[p1]' is missing in type 'IClassA'"
let a: IClassA = new A();

Expected behavior:

I think it should work well without any error reporting.

Actual behavior:

It reports an error like this:

[ts]
Type 'A' is not assignable to type 'IClassA'.
  Types of property 'hello' are incompatible.
    Type '() => F<A>' is not assignable to type '() => F<IClassA>'.
      Type 'F<A>' is not assignable to type 'F<IClassA>'.
        The 'this' types of each signature are incompatible.
          Type 'IClassA' is not assignable to type 'A'.
            Property '[p1]' is missing in type 'IClassA'.

[p1 is a private property of the class, how could it lead a conflict with an implemented interface?

Playground Link:

Related Issues:

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Aug 7, 2018
@RyanCavanaugh
Copy link
Member

This is a correct error, oddly enough. Here's a simplified example that still shows the error:

type F<T> = (this: T) => void;
interface IClassA {
    hello(): F<this>;
}

class A implements IClassA {
    p1: string = '';
    public hello(): F<this> {
        return null as any;
    }
}

IClassA says that if you have an IClassA, it's legal to invoke hello with a this type of this (which is implicitly at least IClassA).

Again, oddly, A does not meet this constraint. A also says it's legal to invoke hello with a type of this (which is implicitly at least A). That last part is important. The code you have implies this code should be legal:

class A implements IClassA {
    p1: string = '';
    public hello(): F<this> {
        return () => {
            console.log(this.p1.toLowerCase());
        }
    }
}

let a: IClassA = new A();
let bad: IClassA = { hello: a.hello };
// Runtime error!
bad.hello();

The fix is to specify the type parameter of A's hello:

type F<T> = (this: T) => void;
interface IClassA {
    hello(): F<this>;
}

class A implements IClassA {
    p1: string = '';
    public hello<T extends IClassA>(this: T): F<T> {
        return () => {
            // Would be an error to write this now:
            // console.log(this.p1.toLowerCase());
        }
    }
}

let a: IClassA = new A();
let notbad: IClassA = { hello: a.hello };
// OK
notbad.hello();

@fenying
Copy link
Author

fenying commented Aug 8, 2018

@RyanCavanaugh

Emmmmm, thx for replying....

My question is:

  1. Why it tells Property '[p1]' is missing in type 'IClassA'? It is a private property, how could it affect the interface implement checking?
  2. Why there is no implement error in class definition? If the class A doesn't fit the interface IClassA.

And the error means this must be an instance of interface IClassA, but class A. If class A implements interface IClassA correctly, how could it produce such an error?

@RyanCavanaugh
Copy link
Member

  1. See the example code I posted - it doesn't matter if p1 is private
  2. Classes with this references are secretly generic and TypeScript doesn't realize there's a problem until it's actually instantiated. Ideally it would be flagged at the declaration site but it's unfortunately not practical

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

2 participants