This applies regardless of whether or not I intersect it with some bogus type, or have a reasonable constraint. So none of the following works.
Returning a plain T
export type Constructor<T> = new (...args: any[]) => T
export interface Timestamp {
timestamp: Date;
}
// plain old T
export function Timestamp<T, CT extends Constructor<T>>(Base: CT): Constructor<Timestamp> & Constructor<T> {
return class extends Base {
timestamp = new Date();
}
}
Constructor of T with a bogus object type ({})
export type Constructor<T> = new (...args: any[]) => T
export interface Timestamp {
timestamp: Date;
}
// Constructor of T with a bogus object type
export function Timestamp<T, CT extends Constructor<T & {}>>(Base: CT): Constructor<Timestamp> & Constructor<T> {
return class extends Base {
timestamp = new Date();
}
}
Constructor of T where T is constrained to {}
export type Constructor<T> = new (...args: any[]) => T
export interface Timestamp {
timestamp: Date;
}
export function Timestamp<T extends {}, CT extends Constructor<T>>(Base: CT): Constructor<Timestamp> & Constructor<T> {
return class extends Base {
timestamp = new Date();
}
}
Constructor of T where T is constrained to object (see also #13805)
export type Constructor<T> = new (...args: any[]) => T
export interface Timestamp {
timestamp: Date;
}
export function Timestamp<T extends object, CT extends Constructor<T>>(Base: CT): Constructor<Timestamp> & Constructor<T> {
return class extends Base {
timestamp = new Date();
}
}
@mhegazy mentioned offline that T is useless in these cases anyway. I think that's beside the point, because you can imagine that when actually using the mixin pattern, you will utilize T in some fashion.
Is there a real world scenario for this? Just trying to understand what you want to accomplish. We would obviously need to ensure T is constrained to an object-like type, but otherwise we could probably do this if we think it is worth the added complexity.
@ahejlsberg, here's a real commit where I'm trying to combine mixins and generics:
class Test<T> implements Interface<T> {}
class Demo<T> extends withMixin<T, Constructor<Interface<T>>>(Test) {}
throws the error any is not a constructor function type, even though none of those variables or types are any.
If I try specifying the argument Test, e.g. withMixin<...>(Test<T>), it calls withMixin with the wrong arguments - withMixin(Test(), {}) - which in turn causes an Test cannot be called without new runtime error`.
(I've updated that diff to include the workaround from https://github.com/Microsoft/TypeScript/issues/14017#issuecomment-279226841. To see what I had before, click here.)
Any resolution on this?
I'm trying to do something similar where I want to use a class-mixin in another class-mixin.
A small example here:
class MyClass {
myClass = 1;
}
type Constructor<T extends MyClass> = new (...args: any[]) => T;
function make1<T extends Constructor<MyClass>>(base: T) {
return class extends base {
make1 = 1;
};
}
/**
* [ts] Type '{
* new (...args: any[]): make3<T>.(Anonymous class);
* prototype: make3<any>.(Anonymous class); } & T'
* is not a constructor function type. [2507]
*/
function make2<T extends Constructor<MyClass>>(base: T) {
return class extends make1(base) {
make2 = 2;
};
}
class Test extends MyClass {}
class Test2 extends make2(Test) {
constructor() {
super();
// this.myClass is gone
// this.make1 is gone
// only this.test2 exists
}
}
Anything to say about this? Is there a workaround possible? I'm anxious to hear about this, since we need it.
Another option could be that this is not proper coding, then I'd like to hear about it, as well.
Most helpful comment
Anything to say about this? Is there a workaround possible? I'm anxious to hear about this, since we need it.
Another option could be that this is not proper coding, then I'd like to hear about it, as well.