Home > Net >  When to use classes vs. interfaces in a TypeScript library?
When to use classes vs. interfaces in a TypeScript library?

Time:03-18

I have seen related questions like this one, but they all assume the difference between classes and interfaces are that classes can be instantiated while interfaces cannot. However, take for example @types/google.maps. For example:

declare namespace google.maps {
  class Map extends google.maps.MVCObject {
    constructor(mapDiv: HTMLElement, opts?: google.maps.MapOptions);
    controls: google.maps.MVCArray<any>[];
    data: google.maps.Data;
    fitBounds(
        bounds: google.maps.LatLngBounds|google.maps.LatLngBoundsLiteral,
        padding?: number|google.maps.Padding): void;
    // ...
  }
}
declare namespace google.maps {
  interface MapType {
    alt: string|null;
    getTile(
        tileCoord: google.maps.Point|null, zoom: number,
        ownerDocument: Document|null): Element|null;
    maxZoom: number;
    minZoom: number;
    name: string|null;
    projection: google.maps.Projection|null;
    radius: number;
    releaseTile(tile: Element|null): void;
    tileSize: google.maps.Size|null;
  }
}
declare namespace google.maps {
  interface MapTypeControlOptions {
    mapTypeIds?: (string)[]|null;
    position?: google.maps.ControlPosition|null;
    style?: google.maps.MapTypeControlStyle|null;
  }
}
declare namespace google.maps {
  class MapTypeRegistry extends google.maps.MVCObject {
    set(id: string, mapType: any): void;
  }
}
declare namespace google.maps {
  interface MapsEventListener {
    remove(): void;
  }
}
declare namespace google.maps {
  interface WebglCameraParams extends google.maps.CameraParams {
    lat: number;
    lng: number;
  }
}

Both interfaces and classes:

  • Extend other objects.
  • Have properties.
  • Have methods/functions.
  • Cannot be directly instantiated from these definitions.

Somewhere in the google maps code there is actually a class for both the classes defined here, and the interfaces defined here. It appears that the class definitions are for what a user can instantiate, vs. what the library can internally instantiate (interfaces).

So then, what are the deeper differences between interfaces and classes in these contexts? I get like what the other answer is saying, that classes usually are constructable, but these classes clearly are not directly instantiable. They represent other classes. But the interfaces represent other classes too. So I am confused.

CodePudding user response:

(From your comment):

I'm asking why they defined them as classes and not interfaces.

There's overlap between type, interface, and class (you clearly know that, but I have to start somewhere :-) ), but class does three things rather than just the one thing that interface or type would do. Let's look at a simpler example:

declare namespace example1 {
    class Example1 {
        a: string;
        constructor(a: string);
        static method(): void;
    }
}

That:

  • Defines the type Example1 which describes instances.
  • Defines that there is a runtime binding, Example1, that refers to the constructor function for those instances. (It doesn't create the binding, it just says there is one.)
  • (Implicitly) Defines a type (typeof Example1) that is the type of the constructor function.

If you wanted to write the same thing without class, you'd have to write each of those three things yourself, such as example2 here:

declare namespace example2 {
    // Define the type for instances
    interface Example2 {
        a: string;
    }
    // Define that there is a binding for the constructor function
    let Example2: {                 //
        new (a: string): Example2;  // Define the type for the constructor function
        method(): void;             //
    };                              //
}

You might even go further and declare a named type for the constructor function:

declare namespace example3 {
    // Define the type for instances
    interface Example3 {
        a: string;
    }
    // Define the type for the constructor function
    interface Example3Constructor {
        new (a: string): Example3;
        method(): void;
    }
    // Define that there is a binding for the constructor function
    let Example3: Example3Constructor;
}

(That's what lib.es5.d.ts does for, say, Array, except it uses var instead of let.)

As always when there's more than one way to do something, some folks will do it one way, others will do it another, but there's some argument for using class when it describes all of the parts you're trying to describe at once.

  • Related