0

I want a class that behaves like js Date function:

  1. when called via new it creates an instance of class.
  2. when called as a function it does some static stuff.

How do i implement this interface?

interface A {
    (): string;
    new(arg: number);
    GetValue(): number;
}

current solution that doesn't compile but produces correct js code in playground:

class B implements A {

    private Value: number;

    constructor(arg: number) {
        if (this.constructor == B) {
            this.Value = arg;
        } else {
            return "42";
        } 
    }

    GetValue(): number {
        return this.Value;
    }
} 

1 Answer 1

2

You can't use an ES2015 or later class to let you call a constructor without the new keyword. In Step 2 of Section 9.2.1 of the linked documents, calling a class constructor without the new keyword should result in a TypeError being thrown. (If you target ES5 in TypeScript you'll get something that works at runtime, but if you target ES2015 or above you will get runtime errors. It's best not to do this.) So to implement your interface you will need to use a pre-ES2015 constructor function instead.


By the way, the new(arg: number) signature needs a return type. For example:

interface A {
  (): string;
  new(arg: number): AInstance; // return something
  GetValue(): number;
}
// define the instance type
interface AInstance { 
  instanceMethod(): void;
}

Here's one way to implement that. First, make a class for AInstance:

class _A implements AInstance {
  constructor(arg: number) { } // implement
  instanceMethod() { } // implement
}

Then, make a function that can be called with or without new:

const AFunctionLike =
  function(arg?: number): AInstance | string {
    if (typeof arg !== "undefined") {
      return new _A(arg);
    }
    return "string";
  } as { new(arg: number): AInstance, (): string };

I've decided that if you call AFunctionLike with an argument then you will get an AInstance, otherwise you will get a string. You can also check explicitly for whether new was used, via new.target, if your runtime has support for it.

Also note that I had to assert that AFunctionLike is newable (with the as clause on the last line) since there's currently no other way to tell TypeScript that a standalone function can be called with new.

Now we're almost done. We can declare a value of type A as follows:

const A: A = Object.assign(
  AFunctionLike,
  {
    GetValue() {
      return 1;
    }
  }
);

The value A has been formed by merging AFunctionLike with an object that implements GetValue(). You can use Object.assign or spread syntax to do this merging.


That's it. You can verify that this works at runtime on the TypeScript Playground. Good luck!

Sign up to request clarification or add additional context in comments.

Comments

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.