javascript - Intentando sobrescribir y extender la firma de un método en una clase hija en Typescript
Tengo una clase base que estoy intentando extender:
export class BaseClass<T extends SomeOtherClass> {
constructor(param: ParamType) {
}
doSomething(param1: Param1Type): BaseClass<T> {
// do something with param1;
return this;
}
}
Mi clase:
export class MyClass<T extends SomeOtherClass> extends BaseClass<T> {
constructor(param: ParamType) {
super(param);
}
doSomething(param1: Param1Type, param2: Param2Type): MyClass<T> {
// super.doSomething(param1);
// do something with param2;
return this;
}
}
pero estoy recibiendo una advertencia:
Property 'doSomething' in type 'MyClass<T>' is not assignable to the same property in base type 'BaseClass<T>'.
Type '(param1: Param1Type, param2: Param2Type) => MyClass<T>' is not assignable to type '(param1: Param1Type) => BaseClass<T>'.
¿No es posible extender las firmas de los métodos en TypeScript? ¿Cómo extiendo las capacidades de la BaseClass si necesito agregar un parámetro al método sobrescrito y es esta la forma correcta de llamar al método padre en la sintaxis de ES6? Soy consciente de que antes de ES6 podría haber llamado a BaseClass.prototype.doSomething.call(this, param1).
Respuesta #1
El problema, como han señalado otros, es que si param2 es obligatorio, rompe el polimorfismo:
// We should be able to do this assignment
let baseRef: BaseClass<SomeOtherClass> = new MyClass<SomeOtherClass>("");
baseRef.doSomething("") // param2 is not required by the base class so MyClass will not receive it even though it NEEDS it
Una solución es hacer que el segundo parámetro sea opcional, de modo que la llamada baseRef.doSomething("") sea válida también para el tipo derivado:
export class MyClass<T extends SomeOtherClass> extends BaseClass<T> {
constructor(param: string) {
super(param);
}
doSomething(param1: string, param2?: string): MyClass<T> {
super.doSomething(param1);
return this;
}
}
Una segunda solución, si solo queremos compartir código entre las clases, es prohibir la asignación let baseRef: BaseClass<SomeOtherClass>=new MyClass<SomeOtherClass>(""); al no heredar realmente de BaseClass sino heredar de una clase que excluya el método doSomething:
type PartialBaseClass = new <T> (param: string) => { [P in Exclude<keyof BaseClass<T>, 'doSomething'>] : BaseClass<T>[P] }
const PartialBaseClass:PartialBaseClass = BaseClass
export class MyClass<T extends SomeOtherClass> extends PartialBaseClass<T> {
constructor(param: string) {
super(param);
}
doSomething(param1: string, param2: string): MyClass<T> {
BaseClass.prototype.doSomething.call(this, param1);
return this;
}
}
// This is now invalid !
let baseRef: BaseClass<SomeOtherClass> = new MyClass<SomeOtherClass>("") ;
