The built-in type number
in Flow allows the "exotic" values such as Infinity
, -Infinity
and NaN
.
How can I enforce the type to only allow real numbers?
EDIT. This is not a question how to check if a variable is real number. It is about typing with Flow.
I am looking for the way to write my functions like:
// @flow
function sum (x: real, y: real) { ... }
My question is how to define the type real
so it works with Flow (http://flowtype.org/).
You can't do that using Flow. You'll need runtime checks.
See discussion of the issues with real numbers here: https://github.com/facebook/flow/issues/1406
The bottom line is, pretty much any operation on real numbers could result in infinity, so distinguishing between real numbers and NaN / Infinity would not be very useful as it would return a type than is not guaranteed to be real.
For example,
Number.MAX_VALUE + Number.MAX_VALUE === Infinity
-Number.MAX_VALUE - Number.MAX_VALUE === -Infinity
Number.MAX_VALUE * 2 === Infinity
Number.MAX_VALUE / 0.5 === Infinity
Separately from that discussion, Flow doesn't have any facilities to blacklist certain values while allowing other values of the same type. You can only whitelist values, including using unions and intersections.
There is a bit of middle ground you can strike with this with Flow. Yes, you'll need to use a runtime check to pull this off in the end, but you can construct an opaque type that will let Flow enforce you cannot bypass those validation functions. First, in one file, put this:
// @flow
// Define `Int` as an opaque type. Internally, it's just a number.
// It's opaque because only this module can produce values of
// this kind, so in order to obtain an "Int", one _must_ use one of
// these functions, which (at runtime) will guarantee that these
// will always be integers.
export opaque type Int: number = number;
// Here's a function that will convert any number to an Int by running
// a typecheck at runtime and perhaps change the value (by rounding)
// This is the ONLY way of obtaining a value of the type Int
export function int(n: number): Int {
if (!Number.isFinite(n)) {
throw new Error('Not a (finite) number');
}
// Round any real numbers to their nearest int
return Math.round(n);
}
// In your private functions, you can now require Int inputs
export function isPrime(n: Int): boolean {
// In here, you can assume the value of `n` is guaranteed to be an Integer number
for (let i = 2; i < Math.sqrt(n); i++) {
if (n % i === 0) return false;
}
return true;
}
Then, you use those like so:
// @flow
import { int, isPrime } from './lib';
isPrime(int(NaN)); // ok, but a runtime error, because NaN is not a number!
isPrime(int(3.14)); // ok, will effectively become isPrime(3)
isPrime(3.14); // Flow error!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With