Typescript umožňuje používať správne viacnásobné dedenie pomocou mixinov, ale zlyháva pri vytváraní deklarácií súborov


Hral som sa s „viacerými dedičnosťami“ v TypeScripte, alebo skôr som sa snažil poriadne pochopiť mixiny. Po mnohých odbočkách som zistil, že najjednoduchší spôsob je použiť čo najmenej explicitného pretypovania a podarilo sa mi vytvoriť veci, ako je uvedené nižšie (kompletné príklady nájdete v tomto giste).

Moja otázka je: Prečo mi TypeScript umožňuje toto vytvoriť, ale potom zlyhá pri vytváraní deklarácií súborov pre toto?

export function TaggedMixin<Super extends Ctor>(superClass: Super) {
  class Tagged extends superClass {
    public static TAG_PUBLIC: string;
    protected static TAG_PROTECTED: string;
    private static TAG_PRIVATE: string;

    public tag_public!: string;
    protected tag_protected!: number;
    private tag_private!: number;
  }

  return Tagged;
}

const Tagged = TaggedMixin(class {
    public static ANON_PUBLIC: string;
    protected static ANON_PROTECTED: string;
    private static ANON_PRIVATE: string;

    public anon_public!: string;
    protected anon_protected!: number;
    private anon_private!: number;
});

class TaggedClass extends Tagged {
    constructor() {
        super();

        TaggedClass.ANON_PUBLIC;
        TaggedClass.ANON_PROTECTED;
        TaggedClass.TAG_PUBLIC;
        TaggedClass.TAG_PROTECTED;

        this.anon_public;
        this.anon_protected;
        this.tag_public;
        this.tag_protected;
    }
}


UPRAVIŤ :

Chyba s týmto TS spôsobuje, že sa nedajú vytvoriť súbory deklarácií:

Property 'tag_protected' of exported class expression may not be private or protected.ts(4094)
Property 'tag_private' of exported class expression may not be private or protected.ts(4094)
Property 'TAG_PROTECTED' of exported class expression may not be private or protected.ts(4094)
Property 'TAG_PRIVATE' of exported class expression may not be private or protected.ts(4094)

Odpoveď č. 1

Existujú obmedzenia pri vydávaní deklarácií a mixinov. Výrazy tried nemôžu mať súkromné alebo chránené členy, pretože ako je uvedené tu:

Exportované anonymné triedy nemôžu mať súkromné alebo chránené členy, ak je zapnuté emitovanie deklarácie, pretože nie je spôsob, ako to reprezentovať v súbore .d.ts.

Typescript bude predstavovať typ inštancie mixinu ako objektový typ, a objektové typy nemôžu mať členov označených ako private alebo protected, odtiaľ táto chyba.

Odstránenie neprístupných členov vyrieši problém.

Môžete tiež pridať nejaké manuálne písanie spolu s deklaráciou triedy, čo vás priblíži k vášmu cieľu:

type Ctor = new (...a: any[]) => any
declare class TaggedMixinInstance { // just a dummy, never call new TaggedMixinInstance() or extend this directly 
    public tag_public: string;
    protected tag_protected: number;
    private tag_private: number;
}

export function TaggedMixin<Super extends Ctor>(superClass: Super): {
    new(...a: any[]): TaggedMixinInstance;
    TAG_PUBLIC: string
} & Super {
    class Tagged extends superClass {
        public static TAG_PUBLIC: string;
        protected static TAG_PROTECTED: string;
        private static TAG_PRIVATE: string;

        public tag_public!: string;
        protected tag_protected!: number;
        private tag_private!: number;
    }

    return Tagged;
}

const Tagged = TaggedMixin(class {
    public static ANON_PUBLIC: string;
    protected static ANON_PROTECTED: string;
    private static ANON_PRIVATE: string;

    public anon_public!: string;
    protected anon_protected!: number;
    private anon_private!: number;
});

class TaggedClass extends Tagged {
    constructor() {
        super();

        TaggedClass.ANON_PUBLIC;
        TaggedClass.ANON_PROTECTED;
        TaggedClass.TAG_PUBLIC;
        TaggedClass.TAG_PROTECTED; // still an error

        this.anon_public;
        this.anon_protected;
        this.tag_public;
        this.tag_protected;
    }
}

Odpoveď č. 2

Len si zahraj s typom Omit 😎. Len príklad s vitest :

import { vi as _vi } from 'vitest';

type VitestUtils = Omit<
typeof _vi,
'_timers' | '_mockedDate' | '_mocker' | 'getImporter' | '_config' // never also works fine
>;
type Primitive = number | boolean | string;
type Props = {
[key in string]?:
 | Primitive
 | Primitive[]
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | ((this: VitestUtils, ...args: any) => any);
};

export default function extendVitest<T extends Props>(options: T) {
 return Object.assign(_vi as VitestUtils, options);
}

Poznámka: Ak nepoznáte súkromné členy triedy, jednoducho dajte never ako druhý parameter Omit.

Súvisiace články