Skip to content

Mixin can't share generic with base class #13979

Closed
@maier49

Description

@maier49

TypeScript Version: 2.2.0-dev.20170209

Code

export class GenericClass<T> {
	value: T;
}
export interface GenericTrait<T> {
	newGenericValue: T;
}

export type Constructor<T> = new (...args: any[]) => T;
export type Constructable = new (...args: any[]) => object;

export function SimpleMixin<C extends Constructable>(base: C): C & Constructor<GenericTrait<any>> {
	return class extends base {
		newGenericValue: any;
		constructor(...args: any[]) {
			super(...args);
		}
	};
}

class Child<T> extends GenericClass<T> {
	child: T;
	constructor(options: { childValue: string }) {
		super();
	}
}

const SimpleMixedClass = SimpleMixin(Child);

let simpleMixedInstance = new SimpleMixedClass<string>({ childValue: 'three' });
// Constructor is maintained
//new SimpleMixedClass(3); // Compilation error - good
// Generic is maintained
//simpleMixedInstance.child = 3; // Compilation error - good
// New generic is lost
simpleMixedInstance.newGenericValue = 3; // Should be a compilation error

My attempt at a workaround

export function MixinGeneric<C extends GenericClass<any>>(base: Constructor<C>): { new <T>(): (GenericClass<T> & GenericTrait<T> & C) } {
	return <any> class extends (base as Constructor<GenericClass<any>>) {
		newGenericValue: any;
		constructor(...args: any[]) {
			super(...args);
		}
	};
}
const MixedClass = MixinGeneric(Child);
// Child constructor is lost
// let mixedInstance = new MixedClass<string>({ childValue: 'value' }); // Compilation error - bad
let mixedInstance = new MixedClass<string>();
// Child generic type is lost
mixedInstance.child = { any: 'value' }; // Should be a compilation error
// New generic type is maintained
// mixedInstance.newGenericValue = { anything: 'value' }; // Compilation error - good

Expected behavior:
While I'm not sure if it's the expected behavior currently, the desired behavior is that the generic provided at instantiation time to the SimpleMixedClass will be used as the generic for the GenericTrait as well as the GenericClass.

Actual behavior:

The generic for the GenericTrait is set to any.

The second code example is my attempt at a workaround, but in that case the constructor/generic for any extensions to the targeted base class are lost.

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions