Typescript tillåter att använda riktig multipel arv med mixins, men misslyckas med att skapa deklarationsfiler
Jag lekte med "multipelt arv" i TypeScript eller snarare försökte få en bra förståelse för mixins. Efter många kringvägar upptäckte jag att det enklaste sättet är att använda så lite explicit typkonvertering som möjligt och jag kunde skapa saker som de som finns nedan (de kompletta exemplen kan hittas i denna gist).
Min fråga är: Varför tillåter TypeScript mig att bygga detta, men misslyckas sedan med att skapa deklarationsfiler för detta?
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;
}
}
REDIGERA :
Felet med den TS misslyckas med att skapa deklarationsfiler:
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)
Svar #1
Det finns begränsningar med att emittera deklarationer och mixins. Klassexpressioner kan inte ha privata eller skyddade medlemmar eftersom som anges här:
Exporterade anonyma klasser kan inte ha privata eller skyddade medlemmar om deklarationsutskick är aktiverat, eftersom det inte finns något sätt att representera det i en .d.ts-fil.
Typescript kommer att representera instanstypen av mixinen som en objekttyp, och objekttyper kan inte ha medlemmar markerade med private eller protected, därav felet.
Att ta bort de icke-offentliga medlemmarna kommer att åtgärda problemet.
Du kan också lägga till viss manuell inmatning med och en klassdeklaration, det kommer att ta dig nära ditt mål:
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;
}
}
Svar #2
Bara lek med Omit-typen 😎. Bara ett exempel med 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);
}
NB: Om du inte känner till klassens privata medlemmar, sätt bara never som andra parameter i Omit.
