1

Say I have this code:

export interface ImportedType {
  new (name: string);
}
export interface Extended<T> {
  foo: (value: any) => any;
}

export function doSomething<T>(Class: T & ImportedType): Extended<T> {
  let name = Class.name;


  Class.foo = function _for(value) {
    // do stuff
  };

  return Class;   // How do I return the extended class?

Typescript is obviously complaining that foo does not exist on ImportedType.

What is the best way to add functionality to the class argument that is passed into doSomething?

Also how can I return the extended type?

5
  • is TypeClass<T> an error? Should it be Extended<T> ? Commented Feb 25, 2018 at 12:41
  • sorry, yes, I'm badly copying Commented Feb 25, 2018 at 12:42
  • @WilhelmOlejnik fixed, thanks! Commented Feb 25, 2018 at 12:42
  • Class is an instance of a class? Or you want it to be the actual class which you want to extend with foo ? Commented Feb 25, 2018 at 12:59
  • @TitianCernicova-Dragomir class is a class, not an instance Commented Feb 25, 2018 at 13:05

2 Answers 2

3

What you are looking for are mixins, this PR explains how to achieve deriving from a generic parameter. There are several changes that must be made to your code and to the code in the PR for your scenario:

  • The ImportedType interface which represents must be generic in order to flow information about the type created by the constructor
  • The PR sample does not constrain the parameters to the constructor at all, and you want to ensure only constructors with a single name parameter are passed, if you just replace the general signature with yours, an errors occurs, I did however find a work around.

Implementation:

export interface ImportedType<T> {
    new(name: string): T;
}
export interface Extended<T>{
    foo: (value: any) => any;
    bar(other: T): T; // If Extended also has T in the interface
}
function doSomething<T>(cls: ImportedType<T>) {
    function mixin<U extends new(...args: any[]) => any>(Base: U) {
        return class ExtendedClass extends Base implements Extended<ExtendedClass> {
            constructor(...args: any[]) {
                super(...args);                }
            foo (value: any) : any {

            }
            bar(other: ExtendedClass): ExtendedClass {
                return this;
            }
        }
    }
    return mixin(cls)
} 


//Usage:

class Foo {
    static fooStatic: string;
    constructor(public name: string) {

    }
    public getName(): string {
        return this.name;
    }
}

let FooExt = doSomething(Foo);

var foo = new FooExt("");

foo.getName(); // from original version 
foo.foo(0) // from extended version 
FooExt.fooStatic // Statics will not work unfortunately 

Edit

If you want to add static methods to the class, you can do it like this:

export interface ImportedType{
    new(name: string): any;
}
export interface Extended{
    foo: (value: any) => any;
}
function staticMixin<T extends ImportedType>(cls: T) : T & Extended{
    let extCls = <T & Extended> cls;
    extCls.foo = (v: any) => {
        console.log("Here");
    }

    return  extCls;
}
// Usage
let FooExtStatic = staticMixin(Foo);
var foo2 = new FooExtStatic(""); //ok
FooExtStatic.foo(0); // static method
FooExtStatic.fooStatic // also works
Sign up to request clarification or add additional context in comments.

3 Comments

foo and bar are class methods and not instance methods unfortunately
@dagda1 And you need T in Extended<T> to be the class type or the type ? Ex for class Foo{} T should be Foo or T should be typeof Foo ? Or do you need it at all ?
@dagda1 added a version for static extensions without T on Extended, I can get the type of Foo but I can only do it using consitional types in 2.8, if you only need typeof Foo in Extended that is already available in staticMixin as T
0
export function doSomething<T>(Class: T & ImportedType): Extended<T> {
  let name = Class.name;
  let extended: any = Class;

  extended.foo = function _for(value) {
    // do stuff
  };

  return extended;
}

2 Comments

Don't think this achieves the desired result, the returned type will not type check very well as it will only have methods from ` Extended<T>` not from the original class
@TitianCernicova-Dragomir @Wilhelm Olejnik what I tried doing was let Extended: Partial<Extended<T>> = Class; and then return Extended as Extended<T>; but still I think lacks a bit

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.