Skip to content

TypeScript mixin extends other mixin: inconsistent “conflicting declarations” error #22845

@cspotcode

Description

@cspotcode

TypeScript Version: typescript@2.9.0-dev.20180323

Search Terms:

mixin extends mixin
mixin conflicting declarations
mixin base contraint

Summary

I want to write two mixin classes, Foo and Bar. Bar requires access to the protected interface of Foo. Therefore I use TS2.8 conditional types to extract the type of a Foo class and use this type as a generic constraint for the Bar mixin's Base class.

Unfortunately, this causes mysteriously inconsistent "conflicting declaration" errors. The inconsistency suggests this is a bug instead of an intended type warning.

Code

class BaseClass {/* ... */}

/* Type error highlighted on "Composed" */
class Composed extends BarMixin(FooMixin(BaseClass)) {}

type Constructor<I = {}> = new (...args: any[]) => I;

interface EmptyInterface {}

/** Mixin */
function FooMixin<C extends Constructor>(Base: C) {
    return class extends Base {
        private _referenceToEmptyInterface: EmptyInterface = {}; // Causes the error.  Why?
        private _fooPrivate: number = 0; // Does not cause an error.  Why not, given the above?
        protected _fooProtected: number = 0;
    }
}

/** Type of class that creates instances of type Foo */
type FooConstructor = typeof FooMixin extends (a: Constructor) => infer Cls ? Cls : never;

/** Mixin that can only extend subclasses of Foo */
function BarMixin<C extends FooConstructor>(Base: C) {
    return class extends Base {
        barMethod() {
            // We require `Base` to implement `Foo` because we need access to protected `Foo` properties.
            this._fooProtected++;
        }
    }
}

Expected behavior:

No errors.

Actual behavior:

index.ts(4,7): error TS2415: Class 'Composed' incorrectly extends base class 'BarMixin<{ new (...args: any[]): FooMixin<typeof BaseClass>.(Anonymous class); prototype: FooMixi...'.
  Type 'Composed' is not assignable to type 'BarMixin<{ new (...args: any[]): FooMixin<typeof BaseClass>.(Anonymous class); prototype: FooMixi...'.
    Property '_referenceToEmptyInterface' has conflicting declarations and is inaccessible in type 'Composed'.

Playground Link (will not work until the playground is updated to TS2.8)

Related Issues:

I asked on StackOverflow: https://stackoverflow.com/questions/49458031/

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFixedA PR has been merged for this issue

Type

No type
No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions