donnut
donnut

Reputation: 720

TypeScript - how to type a self-instantiating function

Function Register is instantiated by calling Register('something'). Typescript says this is only possible if new is used on a void returning function . In this case Register is returning an instance of itself. How should I type this in Typescript?

module Register {

   export function Register(x: string): Instance {
      if (!(this instanceof Register)) {
          return new Register(x)
      }
      this.value = x
   }
   ...

   export interface Instance {
      new(x: string): Instance;
      ...
   }

}
export = Register

Upvotes: 3

Views: 8662

Answers (2)

Cameron Tacklind
Cameron Tacklind

Reputation: 7234

I suspect you're having the same problem that I ran into (that brought me here).

I was trying to add some TypeScript type definitions (.d.ts) for an existing pure JavaScript (.js) project.

The JavaScript source was a traditional constructor function (with prototype class methods) that detected if it was called without new and did the right thing:

const MyObject = function (options) {
    if (!(this instanceof MyObject)) {
        return new MyObject(options);
    }

    // ...
}

MyObject.prototype.myMethod = function() {
    // Do something...
}

module.exports = MyObject;

My understanding of the "correct" way to add TypeScript types for this kind of JavaScript object is with an interface:

declare interface MyObject {
  /**
   * Constructor interface
   */
  new (options?: Options): this;

  /**
   * Functional interface
   */
  (options?: Options): this;

  /**
   * JSDoc ftw!
   */
  myMethod(): void;
}

// Needed so we can export a variable, not just a type
declare const MyObject: MyObject;

// Since the JavaScript is not exporting a "module" (object with a `default` parameter):
export = MyObject;

Upvotes: 0

Faris Zacina
Faris Zacina

Reputation: 14274

Maybe your example is simplified, and you are trying to achieve something more complex under the hood, but If i understand your code correctly you just want to return an instance of the Register function without the New operator.

The only option i can think of is to trick the TS compiler, and to specify the return type as void, and then in your variables use type any.

module Register {

    export function Register(x: string): void {
        if (!(this instanceof Register)) {
            return new Register(x);
        }
        this.value = x;
    }

    export interface Instance {
        new (x: string): Instance;
        value: string;
    }

}

export = Register;

var rega: any = Register.Register("something");
console.log(rega.value); // something

Update: Since you have a problem with specifying any as the explicit type for each variable, then you could use Object.create() instead of the new operator:

module Register {

    export function Register(x: string): Instance {
        var r = Object.create(Register);
        r.value = x;
        return r;
    }

    export interface Instance {
        new (x: string): Instance;
        value: string;
    }

}

export = Register;

var rega = Register.Register("something");

console.log(rega.value); // something

Upvotes: 1

Related Questions