TypeScript mixin extends other mixin: inconsistent "conflicting declarations" error

538 views Asked by At

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. Why? Is this a TS bug? Can I work around it?

/*
* Why is this type error happening?
*   Class 'Composed' incorrectly extends base class 'Bar...
*     Type 'Composed' is not assignable to type 'Bar...
*       Property '_referenceToEmptyInterface' has conflicting declarations and is inaccessible in type 'Composed'.
*/
class Composed extends BarMixin(FooMixin(EventEmitter)) {}

import { EventEmitter } from "events";
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++;
        }
    }
}

_referenceToEmptyInterface is flagged as a duplicate declaration. However, _fooPrivate is not. The only difference is that the former's type refers to an interface declaration, whereas the latter's type is a primitive. The interface declaration is totally empty and is declared outside of the mixin function, so all declarations of _referenceToEmptyInterface should be identical.

Is this a bug in the compiler? How should I handle this situation?

EDIT: related GH issue: https://github.com/Microsoft/TypeScript/issues/22845

0

There are 0 answers