Why private/protected members must originate from the same class for type compatibility?

In TypeScript Handbook, it says:

Private and protected members in a class affect their compatibility. When an instance of a class is checked for compatibility, if the instance contains a private member, then the target type must also contain a private member that originated from the same class. Likewise, the same applies for an instance with a protected member. This allows a class to be assignment compatible with its super class, but not with classes from a different inheritance hierarchy which otherwise have the same shape.

While type compatibility in TypeScript being structural (duck typing):

class Animal {
    feet: number;
}

class Size {
    feet: number;
}

let a: Animal = new Size; // Ok!

the cases for class containing protected/private members is actually nominal:

class Animal {
    private feet: number;
}

class Size {
    private feet: number;
}

let a: Animal = new Size; // Error!

Could anyone explain why this inconsistency exists in TypeScript between public and protected/private members?

Answer

This is by design to prevent private/protected members from being accessed from a different type.

From https://github.com/Microsoft/TypeScript/issues/7755:

Consider something like this

class Foo {
    private versionNumber = 10;

    public compareTo(other: Foo) {
        return this.getVersion() === other.getVersion();
    }

    private getVersion() {
        return this.versionNumber;
    }
}

class Bar {
    private myName = 'default;'
    public compareTo(other: Bar) {
        return this.myName === other.myName;
    }

    private getVersion() {
        /* ... DANGEROUS CODE HERE ...*/
        return -1;
    }
}

By inspection, Bar#getVersion is never invoked — it’s a private method and there are no calls to it from the originating class. That inspection ought to be sufficient for code that doesn’t actively try to work around the typechecker.

But this code does invoke Bar#getVersion:

let f = new Foo();
let b = new Bar();
f.compareTo(b);

Leave a Reply

Your email address will not be published. Required fields are marked *