Search code examples
typescriptabstract-classimplementationprotectedabstraction

Accessing protected members from class implementation in TypeScript


I'm getting this error when trying to implement a protected member in TypeScript. Is there a way to avoid it? Or am I doing something the wrong way here?

It seems that TypeScript does not recognize implementations of a class as its subclass, like it does for extensions.

Class 'MyClassImpl' incorrectly implements class 'MyClass'.
Did you mean to extend 'MyClass' and inherit its members as a subclass?
Property 'myMember' is protected but type 'MyClassImpl' is not a class derived from 'MyClass'.
abstract class MyClass {
  protected abstract myMember: boolean
}

class MyClassImpl implements MyClass {
  /**
   * Class 'MyClassImpl' incorrectly implements class 'MyClass'.
   * Did you mean to extend 'MyClass' and inherit its members as a subclass?
   * Property 'myMember' is protected but type 'MyClassImpl' is not a class derived from 'MyClass'.
   */
  protected myMember: boolean
  constructor() {
    this.myMember = true
  }
}

class MyClassExt extends MyClass {
  /**
   * This way it just works fine, but I wanted to implement a class, not extend it
   */
  protected myMember: boolean
  constructor() {
    super()
    this.myMember = true
  }
}

I tried to implement a protected member of an abstract class, and I expected it to be available for the implementation class. I expected that TypeScript would recognize an implements class as a subclass, just like it does for extends classes.


Solution

  • This behaviour is a bit surprising but it is working as intended.

    The key phrase is "implements checks assignability" – the presence of a protected/private field means that the classes will be compared nominally rather than structurally. Overriding the abstract field requires a different declaration, so it is a different field even though it has the same name.

    It's consistent if a bit unhelpful in this case, but as you've discovered there is an easy enough workaround – using extends instead of implements, so that the protected field is inherited.