Refinements with Flow

Integer = ( number, (n) => n % 1 === 0 )

The idea

// myrefinement.js// private
class IsR {}
// public
export type R = A & IsR;
// public
export function refinement(a: A): ?R {
return predicate(a) ? ((a: any): R) : null
}

A first example, integers

// integer.jsclass IsInteger {}export type Integer = number & IsInteger;export function integer(a: number): ?Integer {
return a % 1 === 0 ? ((a: any): Integer) : null
}
import type { Integer } from './integer.js'
import { integer } from './integer.js'
function foo(n: number) { ... }
function bar(n: Integer) { return n > 0 } // <= n is not boxed
integer(1.1) // => nullconst i = integer(1)
if (typeof i === 'number') {
foo(i) // <= we can call foo because an integer is a number...
bar(i)
}
// ... but a number is not an integer
bar(1) // <= error: number. This type is incompatible with isInteger

Polymorphic refinements

// nonEmptyArray.jsclass IsNonEmptyArray {}export type NonEmptyArray<A> = Array<A> & IsNonEmptyArray;// polymorfic function
export function nonEmptyArray<A>(a: Array<A>): ?NonEmptyArray<A> {
return a.length > 0 ? ((a: any): NonEmptyArray<A>) : null}
import type { NonEmptyArray } from './nonEmptyArray.js'
import { nonEmptyArray } from './nonEmptyArray.js'
function foo<A>(a: NonEmptyArray<A>) { ... }const a = nonEmptyArray([1, 2, 3])
if (a) {
foo(a)
}
// foo([1, 2, 3]) // error

Drawbacks

function foo(x: Object) { ... }const i = integer(1)
if (typeof i === 'number') {
foo(i) // <= Flow doesn't complain
}

--

--

mathematician and rock climber

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store