"constructor" in typescript interface

A constructor is technically a special, static function call that returns an instance of itself, so it doesn't really make sense for it to be part of an interface, because interface members are instance bound.

Typescript has a bit of a compiler trick to make them statically bound, and uses this for ambient declarations.

In your case, to provide an implementation, you need to remove the constructor from the interface:

interface LatLng {
    lat(): number;
    lng(): number;
}

class LatLngImpl implements LatLng {
    constructor(lat: number, lng: number) {

    }

    lat(): number {
        return 0;
    }

    lng(): number {
        return 0;
    }
}

If LatLng is implemented elsewhere, you simply need to provide an ambient declaration:

interface LatLng {
    lat(): number;
    lng(): number;
}

interface LatLngConstructor {
    new(lat: number, lng: number): LatLng;
}

declare var LatLng: LatLngConstructor;

Notice that LatLngConstructor defines a new(...): LatLng, which is what describes the constructor signature.


Where is this library from? Whoever wrote it deserves a stern talking-to. Either it's a mistake (in which case they never tested it) or it's intentional but eeeeevil: using the not-quite-reserved word constructor as an identifier for an instance method is just asking for trouble.

EDIT 2019-04-25: The trouble is worse than just "this is a bad idea"; it actually looks like once JavaScript natively supports class fields, it is going to be an error to have a class with a property named "constructor". TypeScript will change to reflect this in version 3.5+, so the implementation below will stop working then. See recently opened GitHub issue Microsoft/TypeScript#31020 for more information. After TS3.5 it looks like there will be no way to have a class with an instance constructor property that is not the actual constructor function itself.

Either the library author intended to refer to a real constructor that you call with new, in which case they should do something like @Series0ne's suggestion; or the author really wants to use an instance method to initialize the object, in which case they should do everyone a favor and use a more conventional method name like init().

In either case nobody should accept the interface you've been given, and certainly nobody should implement it.

Let's implement it:

export class LatLngImpl implements LatLng {
  private _lat: number;
  private _lng: number;
  lat() {
    return this._lat;
  }
  lng() {
    return this._lng;
  }
  // here's the important part, but see Microsoft/TypeScript#31020
  "constructor"(lat: number, lng: number) { 
    this._lat = lat;
    this._lng = lng;
  }
}

The trick is to use the string literal "constructor" instead of the bare word constructor. From the TypeScript spec:

String literals may be used to give properties names that are not valid identifiers

By using the string literal, we were able to declare it as an instance method, and not the static class constructor method that is invoked when you call new, and it compiles happily.

Now we can use it, if we dare:

const latLng = new LatLngImpl();
latLng.constructor(47.6391132, -122.1284311); // 😠 Why not init()?
console.log("Latitude: " + latLng.lat() + ", Longitude: " + latLng.lng());

Yuck, we shouldn't have dared.

EDIT AGAIN 2019-04-25 The above quoted-string implementation will not work starting in TypeScript 3.5, and the correct answer to this will be "you can't do this" and "use a real constructor instead".

Hope that helps; good luck!

Tags:

Typescript