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

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions