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

Extended class cannot read protected parent class field #8512

Closed
cevek opened this issue May 7, 2016 · 5 comments
Closed

Extended class cannot read protected parent class field #8512

cevek opened this issue May 7, 2016 · 5 comments
Labels
Canonical This issue contains a lengthy and complete description of a particular problem, solution, or design Question An issue which isn't directly actionable in code

Comments

@cevek
Copy link

cevek commented May 7, 2016

1.8.0-beta

class Foo {
    protected status: string;
    protected foo: Foo;
}

class Bar extends Foo{
    update() {
        this.foo.status 
            // Property 'status' is protected and only accessible through an instance of class 'Bar'.
    }   
}

Why this error happen? So Bar yet extends Foo

@mhegazy mhegazy added the Question An issue which isn't directly actionable in code label May 7, 2016
@mhegazy
Copy link
Contributor

mhegazy commented May 7, 2016

As the error message states, there are two constraints on accessing a protected, 1. within a derived class (which is true) and 2. through an instance of the derived class (foo does not satisfy this).

The second restriction to ensure that class isolation is not violated. consider this:

class Base {
    protected state: number | string;
    protected doAction() { }
}

class Derived1 extends Base {
    constructor(a: Base) {
       a.state = "name"; // string is a valid value for Base  
       a.doAction(); 
   }
}

class Derived2 extends Base {
    state: number = 0; // more specific than Base
    doAction() {
       this.state.toFixed();  // state has to be a number;
    }
}

new Derived1(new Derived2()); /// Error!!! TypeError: this.state.toFixed is not a function

This a common OO concept and is not specific to TS; you can find more information about this in:

@mhegazy mhegazy closed this as completed May 7, 2016
@mhegazy mhegazy added the Canonical This issue contains a lengthy and complete description of a particular problem, solution, or design label May 7, 2016
@cevek
Copy link
Author

cevek commented May 8, 2016

thanks for detailed explanation

@0815fox
Copy link

0815fox commented Oct 25, 2016

I just was sent over from #11832. I first thought, that the example presented here by @mhegazy explains, why an access to protected is not possible. However, the example rather shows, that narrowing a type must not be allowed. See the following example:

class Base {
    protected state: number | string;
    doAction1() { this.state = 'This assignment is valid'; }
}

class Derived extends Base {
    state: number = 0; // more specific than Base
    doAction2() {
       console.log(this.state);
       this.state.toFixed();  // state has to be a number;
    }
}

const Inst = new Derived();
Inst.doAction1()
Inst.doAction2();

Command line:

michael@mksde01:~/tmp/tstest$ tsc -v
Version 2.0.3
michael@mksde01:~/tmp/tstest$ tsc test.ts
michael@mksde01:~/tmp/tstest$ node test.js
This assignment is valid
/home/michael/tmp/tstest/test.js:20
        this.state.toFixed(); // state has to be a number;
                   ^

TypeError: this.state.toFixed is not a function
    at Derived.doAction2 (/home/michael/tmp/tstest/test.js:20:20)
    at Object.<anonymous> (/home/michael/tmp/tstest/test.js:26:6)
    at Module._compile (module.js:541:32)
    at Object.Module._extensions..js (module.js:550:10)
    at Module.load (module.js:458:32)
    at tryModuleLoad (module.js:417:12)
    at Function.Module._load (module.js:409:3)
    at Module.runMain (module.js:575:10)
    at run (bootstrap_node.js:352:7)
    at startup (bootstrap_node.js:144:9)

Is there an explanation for this?

Actually I never tried that, because I did not expect it to work. But it compiles just fine and crashes at runtime...

@RyanCavanaugh
Copy link
Member

@0815fox aliasing through a base reference when the underlying fields are mutable isn't sound. It's a complexity / soundness tradeoff. The alternative is to make code like this an error, which is cumbersome because it's much more common that fields aren't mutated

function process(x: { a: string | number}) {
    console.log(x.a);
}

function fn(x: { a: string }) {
    process(x);
}

@0815fox
Copy link

0815fox commented Oct 25, 2016

On my opinion, there should be a difference between the following two function definitions:

let x:{a:string|number} = {a:42}; //should be valid
let y:{a:string}|{a:number} = {a:42}; //should be valid
x.a = 'foo'; //should be valid
y.a = 'foo'; //should be invalid, as it is unsure, whether y is of the first or the second type
(<{a:string}>y).a = 'foo'; //required to do this instead
y.a = 42; //should be invalid, as it is unsure, whether y is of the first or the second type
(<{a:number}>y).a = 42; //required to do this instead

The equivalence {a:string|number} = {a:string}|{a:number} should only be assumed for read accesses; for write accesses there should be a difference. However, this would only be really usable combined with a const keyword for function arguments and interface members.

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Canonical This issue contains a lengthy and complete description of a particular problem, solution, or design Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

4 participants