Marcus Junius Brutus Marcus Junius Brutus - 4 months ago 9
Javascript Question

how to declare that a given class implements an interface in Facebook Flow?

I have the following code using Flow:

// @flow
'use strict';

import assert from 'assert';

declare interface IPoint {
x: number;
y: number;
distanceTo(other: IPoint): number;
}

class Point {
x: number;
y: number;
distanceTo(a: IPoint): number {
return distance(this, a);
}
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}


function distance(p1: IPoint, p2: IPoint): number {
function sq(x: number): number {
return x*x;
}
return Math.sqrt( sq(p2.x-p1.x)+sq(p2.y-p1.y) );
}

assert(distance ( new Point(0,0), new Point(3,4))===5);
// distance ( new Point(3,3), 3); // Flow complains, as expected
assert((new Point(0,1)).distanceTo(new Point(3,5))===5);
// (new Point(0,1)).distanceTo(3); // Flow complains as expected


Running
npm run flow
yields no complains as expected, whereas the commented-out lines give rise to warnings (again, as expected).

So all's well with the world except that I don't know how to make it explicit at the point where class
Point
is defined that it is "implementing" interface
IPoint
. Is there a way to do so or is it not idiomatic?

Answer

Here is the simplest way to do it:

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        (this: IPoint);
        this.x = x;
        this.y = y;
    }
}

The key part is (this: IPoint). From JS VM's point of view it's just an expression that does nothing, but Flow needs to check if casting this to IPoint is valid, effectively checking if class implements IPoint interface.