DaveJ
DaveJ

Reputation: 2427

Type Refinements in Flow

I'm getting an error that 'The operand of an arithmetic operation must be a number.' but at the start of the function I have a runtime check to ensure that this.startedDateTime is a number. I'm confused about why this type refinement doesn't work.

/* @flow */

class Transfer {
    startedDateTime: ?number;

    start() {
        this.startedDateTime = new Date().getTime();
    }

    get elapsedTime(): ?number {
        if (typeof this.startedDateTime === 'number') {
            const currentDateTime: number = new Date().getTime();
            const elapsedMs: number = this.startedDateTime - currentDateTime;
            return elapsedMs;
        }
        return null;
    }
}

Try it here.

Upvotes: 1

Views: 262

Answers (2)

user6445533
user6445533

Reputation:

The problem is that type refinements are invalidated with the first subsequent function call - Date().getTime() in your example.

Functions/methods aren't pure in Javascript but may perform side effects. Date().getTime() for example may delete this.startedDateTime or set it to null. Consequently flow invalidates your refinement to preserve type safety.

To bypass this behavior you can store the property in a constant before any function invocation:

/* @flow */

class Transfer {
  startedDateTime: ?number;

  start() {
    this.startedDateTime = new Date().getTime();
  }

  get elapsedTime(): ?number {
    if (typeof this.startedDateTime === 'number') {
      const startedDateTime = this.startedDateTime;
  //  ^^^^^^^^^^^^^^^^^^^^^
      const currentDateTime: number = new Date().getTime();
      const elapsedMs: number = startedDateTime - currentDateTime;
  //                            ^^^^^^^^^^^^^^^
      return elapsedMs;
    }
    return null;
  }
}

Try it

Upvotes: 3

Marko Savic
Marko Savic

Reputation: 2394

In your approach startedDateTime can be: number, null or undefined, because you are using Flow Maybe Types. You just need to say: startedDateTime: number;.

Here you are a example.

Maybe you want to do this:

class Transfer {
    startedDateTime: ?number;
    start() {
        return this.startedDateTime = new Date().getTime();
    }
    get elapsedTime(): ?number {
        if (typeof this.startedDateTime === 'number') {
            const currentDateTime: number = new Date().getTime();
            const elapsedMs: number = this.start() - currentDateTime;
            return elapsedMs;
        }
        return null;
    }
}

Upvotes: 1

Related Questions