Amestecă într-o clasă de bază abstractă în Typescript


Aș dori să amestec câteva metode într-o clasă abstractă de bază, pentru a crea o nouă clasă abstractă.

Luați următorul exemplu:

abstract class Base {
    abstract method();
}

interface Feature {
    featureMethod();
}

class Implementation extends Base implements Feature {
    method() {
    }

    featureMethod() {
       // re-usable code that uses method() call
       this.method();
    }
}

Acest lucru funcționează bine, dar scopul este să luăm implementările interfeței Feature și să le mutăm într-un mixin astfel încât să poată fi reutilizate de alte implementări ale clasei Base.

Am ajuns la următoarele, dar nu se compilează în Typescript 2.4.1

type BaseConstructor<T = Base > = new (...args: any[]) => T;
export function MixFeature<BaseType extends BaseConstructor>(TheBase: BaseType) {
    abstract class Mixed extends TheBase implements Feature {
        featureMethod() {
            // re-usable code that uses method() call
            this.method();
        }
    }
    return Mixed;
}

class Implementation extends MixFeature(Base) {
    method() {
    }
}

Dar Typescript nu aprobă, spunând:

Error:(59, 41) TS2345:Argument of type 'typeof Base' is not assignable to parameter of type 'BaseConstructor<Base>'.
Cannot assign an abstract constructor type to a non-abstract constructor type.

Este posibil să facem să funcționeze acest lucru sau este o limitare a Typescript că bazele abstracte nu pot fi extinse folosind mixinuri?

Răspuns #1

În prezent nu există nicio modalitate de a descrie tipul unui constructor de clasă abstractă în TypeScript. Problema GitHub Microsoft/TypeScript#5843 urmărește acest aspect. Poți să te uiți acolo pentru idei. O sugestie este că poți suprima eroarea pur și simplu afirmând că Base este un BaseConstructor:

// no error
class Implementation extends MixFeature(Base as BaseConstructor) {
    method() {
    }
}

Acum codul tău se compilează. Dar reține că, deoarece nu există nicio modalitate de a specifica că BaseConstructor reprezintă un constructor abstract, clasa returnată va fi interpretată ca fiind concretă indiferent dacă vrei sau nu, în ciuda faptului că Mixed este declarată ca abstract:

// also no error; may be surprising
new (MixFeature(Base as BaseConstructor));

Așadar, deocamdată va trebui să fii atent dacă vrei să folosești mixins cu clase abstracte. Mult noroc!