Must specify Extended class type in child Class method return with TS 2.4

990 views Asked by At

I'm trying to migrate from TypeScript 2.3.2 to 2.4.2.

This code used to work in 2.3:

class Records {
  public save(): Records {
    return this;
  }
}

class User extends Records {
  public update(): User {
    return this.save();
  }
}

let myUser: User = new User();
let obj = myUser.update();

but now, it raises the following error on compilation:

Error:(10, 5) TS2322: Type 'Records' is not assignable to type 'User'. Property 'update' is missing in type 'Records'.

Of course, I can change update so that it returns "Records" like this:

  public update(): Records {
    return this.save();
  }

But I don't really get why I have to change this. It looks like a regression to me. Anyone have an explanation or a better solution ? I find it makes my code harder to read.

Thanks a lot,

Julien

3

There are 3 answers

4
vince On

It's a typing issue. You are saying that User.update will return a User, but it actually returns the result of Records.save, which is of type Records. The Records type cannot be assigned to something expecting a User because Records does not have the update method.

In order to fix this, you must specify the return type of update to be Records (which it is currently returning) or you will need to modify Records.save to return a User (which will require changing the implementation of the method) -- your solution will depend on what you need from your program.


I think part of your confusion may come from the extends keyword -- it may seem like User and Records are kind of the same since User "extends" Records, but this is not the case. Read more about extends in the "Inheritance" section of this article: https://www.typescriptlang.org/docs/handbook/classes.html


In response to your comments (assuming you cannot change the implementation), a more accurate type for your save method would be a Union type of User | Records since it will return one or the other depending on the context in which it is called.

That would look like this:

public save(): Records | User {
  return this;
}

Read about Union types here: https://www.typescriptlang.org/docs/handbook/advanced-types.html

0
kitko112 On

In OOP, the base class won't know what the new methods and new properties would be on the derived class, that's why complier complain you.

How about return 'this'? Is it possible for you?

class User extends Records {
   public update(): User {
      super.save();
      return this;
   }
}
0
Mouneer On

The ideal solution to your problem is to just remove the return type of save() method.

Check it live (No Errors anymore)

Explanation: When the method becomes:

public save() { // With no explicit return type.
    return this;
}

This means that typescript "Type Inference" feature will infer the type to be Records or any child class of type Records.