0

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.

henrique
  • 28
  • 3

1 Answers1

1

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.

motto
  • 2,888
  • 2
  • 2
  • 14
  • I have to say I'm not fully pleased with that workaround, since its sounds like a semantical mistake for me (I'm implementing a class, not extending it). Also, it will require the use of the `super()` call on the constructor and maybe other side effects we might not be aware yet. The solution that I chose to use on my project (also not semantically perfect) was to mark the member as `public readonly` instead of `protected`... – henrique Mar 02 '23 at 15:48
  • I was considering open an issue on TypeScript repository, but I don't want to get a crappy answer like [this](https://github.com/microsoft/TypeScript/issues/37447#issuecomment-600839794) – henrique Mar 02 '23 at 15:51
  • I think there's arguably a path here to treat `abstract protected` members specially since there's no JS footprint to complicate things, but I imagine the implementation might be obtuse. The `implements` keyword is, of course, misleadingly named when nominal typing gets involved. – motto Mar 02 '23 at 18:02
  • TS has its gaps when considering OOP paradigma, and that's comprehensible since JS is focused on being functional instead of object-oriented. This issue would be avoided if TS had simply implemented the `interface` concept like all the other programming languages. I've just accepted that I won't have a 100% satisfying solution for this case, life goes on... Thanks for the help! – henrique Mar 02 '23 at 19:37