Closed
Description
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:
- Clone the branch https://github.com/microsoft/rushstack/tree/octogonz/ts-37894
rush install
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 themembers
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 usingreadonly
, 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.