Reputation:
Consider the following base interface and abstract class:
export interface IPersonViewModel {
name: string;
setName(value: string): IPersonViewModel;
}
export abstract class PersonViewModel implements IPersonViewModel {
constructor(name: string) {
this.name = name;
}
public name: string;
public setName(value: string): IPersonViewModel {
const vm = { ...this, name: value };
return vm;
}
}
Now consider the following derived class and interface:
export interface IEmployeeViewModel extends IPersonViewModel {
role: string;
}
export class EmployeeViewModel extends PersonViewModel implements IEmployeeViewModel {
constructor(role: string, name: string) {
super(name);
this.role = role;
}
public role: string;
public setRole(value: string): IEmployeeViewModel {
const vm = { ...this, role: value };
return vm;
}
}
If setName is used on an instance of IEmployeeViewModel the return type is IPersonViewModel. Is there any way to use generics or some other methodology such that the return type of the base interface function is the same as the implemented interface i.e. IEmployeeViewModel.setName returns IEmployeeViewModel?
Upvotes: 0
Views: 1333
Reputation: 42208
You don't need generics. All that you need is the polymorhic this
type. Use this
as the return type in both the interface
and the classes.
export interface IPersonViewModel {
name: string;
setName(value: string): this;
}
public setName(value: string): this {
...
Now you are telling TypeScript that the returned type of setName
should match the type of the original object.
const employee = new EmployeeViewModel('someRole', 'someName');
// renamed has type EmployeeViewModel
const renamed = employee.setName('Bob');
You might use generics if you were interacting with an implementation of IPersonViewModel
in a situation where you didn't know what specific implementation you were dealing with.
This sort of function is fine, because the implementation doesn't matter. All IPersonViewModel
objects have a name
and a setName
.
const nameModifyFn = (object: IPersonViewModel): string => {
return object.setName('new name').name;
}
But here you could use a generic so that the return type matches the original type.
const nameModifyFn = <T extends IPersonViewModel>(object: T): T => {
return object.setName('new name');
}
Upvotes: 1