Skip to content

Can't extend mixin constructor that constructs a generic type #13807

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
DanielRosenwasser opened this issue Feb 1, 2017 · 6 comments
Open
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@DanielRosenwasser
Copy link
Member

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();
    }
}
@DanielRosenwasser DanielRosenwasser added the Bug A bug in TypeScript label Feb 1, 2017
@DanielRosenwasser
Copy link
Member Author

@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.

@ahejlsberg
Copy link
Member

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.

@appsforartists
Copy link

@ahejlsberg, here's a real commit where I'm trying to combine mixins and generics:

http://codereview.cc/D3000

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`.

@appsforartists
Copy link

(I've updated that diff to include the workaround from #14017 (comment). To see what I had before, click here.)

appsforartists added a commit to material-motion/material-motion-js that referenced this issue Apr 10, 2017
Summary:
This is the first operator to move into its own mixin (as scaffolded in http://codereview.cc/D2993).

TypeScript doesn't seem to handle the types of mixed classes properly when used with generics: microsoft/TypeScript#13807

Related: #114

Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, shyndman, featherless

Reviewed By: O2 Material Motion, #material_motion, featherless

Subscribers: featherless

Tags: #material_motion

Differential Revision: http://codereview.cc/D3000
@mhegazy mhegazy added Suggestion An idea for TypeScript In Discussion Not yet reached consensus and removed Bug A bug in TypeScript labels Jul 21, 2018
@alber70g
Copy link

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
  }
}

@alber70g
Copy link

alber70g commented Feb 4, 2019

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants