Skip to content

Usage of base class for anonymous class results in erroneous declaration outputΒ #42738

Closed
@TimvdLippe

Description

@TimvdLippe

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

Workbench Repro

πŸ’» 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 {};

Metadata

Metadata

Assignees

Labels

Design LimitationConstraints of the existing architecture prevent this from being fixedRescheduledThis issue was previously scheduled to an earlier milestone

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions