Skip to content

TypeScript 4.0 causes unsolvable TS2611 error for properties inherited from a mixin #41347

Closed
@octogonz

Description

@octogonz

TypeScript Version: 4.0.5

Search Terms: TS2611 mixin

Code

// Mixin utilities
export type Constructor<T = {}> = new (...args: any[]) => T;
export type PropertiesOf<T> = { [K in keyof T]: T[K] };

interface IApiItemConstructor extends Constructor<ApiItem>, PropertiesOf<typeof ApiItem> {}

// Base class
class ApiItem {
  public get members(): ReadonlyArray<ApiItem> {
    return [];
  }  
}

// Normal subclass
class ApiEnumMember extends ApiItem {
}

// Mixin base class
interface ApiItemContainerMixin extends ApiItem {
  readonly members: ReadonlyArray<ApiItem>;
}

function ApiItemContainerMixin<TBaseClass extends IApiItemConstructor>(
  baseClass: TBaseClass  
): TBaseClass & (new (...args: any[]) => ApiItemContainerMixin) {
  abstract class MixedClass extends baseClass implements ApiItemContainerMixin {
    public constructor(...args: any[]) {
      super(...args);
    }

    public get members(): ReadonlyArray<ApiItem> {
      return [];
    }
  }

  return MixedClass;
}

// Subclass inheriting from mixin
export class ApiEnum extends ApiItemContainerMixin(ApiItem) {
  // This worked prior to TypeScript 4.0:
  public get members(): ReadonlyArray<ApiEnumMember> {
    return [];
  }  
}

This is a simplification of a real-world problem encountered with API Extractor, which can be reproed as follows:

  1. Clone the branch https://github.com/microsoft/rushstack/tree/octogonz/ts-37894
  2. rush install
  3. rush build --to api-documenter

Expected behavior:

Using TypeScript 3.x, the above code compiled without errors.

Actual behavior:

Using TypeScript 4.0, we get this error:

'members' is defined as a property in class 'ApiItem & ApiItemContainerMixin', but is overridden here in 'ApiEnum' as an accessor. (TS2611)
  • I was not able to workaround the problem by adding // @ts-ignore to the members property, because // @ts-ignore does not get emitted in the .d.ts file. Thus the error will reappear when a consumer of this API tries to compile the .d.ts file.
  • The consumer of the API cannot add // @ts-ignore, because they cannot edit the .d.ts file.
  • This is a case where a project compiled using TypeScript 3.x cannot be consumed by a project that compiled using TypeScript 4.x.
  • The seemingly "correct" solution would be to declare the interface member as a getter instead of using readonly, but that is not an allowable syntax.

The solution that worked was to delete the ApiItemContainerMixin.members property declaration. This was possible in my case only because the base class ApiItem.member happened to also declare that property. It would not be generally true.

Playground Link: Link

Related Issues: This is a regression introduced by PR #37894. In the PR comments, @sandersn asked me to open a separate issue to track this.

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFix AvailableA PR has been opened for this issueRescheduledThis 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