Closed
Description
Bug Report
π Search Terms
mixin declaration
π Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about v4.2.0-dev.20210207
β― Playground Link
Playground link with relevant code
π» Code
// @showEmit
// @showEmittedFile: MyPanel.d.ts
// @declaration
// @filename: singleton.ts
type Constructor<T = {}> = new (...args: any[]) => T;
export function singleton<TBase extends Constructor>(baseClass: TBase) {
let instance: InstanceType<TBase>;
return class extends baseClass {
static instance(opts: {forceNew?: boolean} = {forceNew: undefined}): InstanceType<TBase> {
const {forceNew} = opts;
if (!instance || forceNew) {
instance = new this() as InstanceType<TBase>;
}
return instance;
}
}
}
// @declaration
// @filename: MyWidget.ts
export class MyWidget {
public publicMethod(): void {
this.someMethod();
}
public someMethod(): void {
}
}
// @declaration
// @filename: MyPanel.ts
import {singleton} from './singleton.js';
import {MyWidget} from './MyWidget.js';
// The `.d.ts` file of `MyPanel.ts` includes a new definition of a MyPanel_Base
export class MyPanel extends singleton(class extends MyWidget {}) {}
// A manual declaration of the base class will work as expected
class MySecondPanelBase extends MyWidget {}
export class MySecondPanel extends singleton(MySecondPanelBase) {}
π Actual behavior
The .d.ts
file for MyPanel
includes a full copy of the anonymous class, including its base class. In other words: anything defined in a base class of an anonymous class that is mixed-in gets copied into the .d.ts
file. A manual class declaration which gets passed into a mixin declaration does not show the same behavior.
declare const MyPanel_base: {
new (...args: any[]): {};
instance(opts?: {
forceNew?: boolean | undefined;
}): {
publicMethod(): void;
someMethod(): void;
};
} & {
new (): {
publicMethod(): void;
someMethod(): void;
};
};
export declare class MyPanel extends MyPanel_base {
}
export {};
π Expected behavior
Mixing an anonymous class with a base class should generate the same .d.ts
with regards to how the base class is mixed-in. In the playground link, that would mean that MyPanel_base
does not copy the two methods defined on MyWidget
and instead extends MyWidget
.
declare class MyPanel_base extends MyWidget {
new (...args: any[]): {};
instance(opts?: {
forceNew?: boolean | undefined;
}): MyPanel_base;
} & typeof MyPanel_base;
export declare class MyPanel extends MyPanel_base {
}
export {};