A refinement type is a type endowed with a predicate which must hold for all instances of the refined type

For example the type *Integer* can be defined as the pair

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

where *number* is the refined type and *(n) => n % 1 === 0* is the predicate.

This is a proof of concept, the goal here is to implement refinements **without boxing values**.

# The idea

A refinement has the following blueprint (where *A* is the refined type)

// 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

}

- calling the function
*refinement*should be the only legal way to get an instance of*R* - the class
*IsR*must not be exported. If the constructor is not exported then users of the library that defined*R*can’t accidentally build a value*v*of type*R*(unless they voluntarily do an unsafe cast with*((v: any): R)*which should be avoided) - collisions between two refinements of the same refined type
*A*are not possible because of their corresponding nominal*IsR*types

# 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

}

Let’s see how you can use the *integer.js* module

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 boxedinteger(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

So an *Integer* is a *number* but a *number* is not an *Integer*. Nice.

# Polymorphic refinements

The refined type *A* can be polymorfic (e.g. *Array<*>)*.

Let’s define a refinement which represents a non empty array

// 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}

Usage

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

Every refinement type is also an *Object*

function foo(x: Object) { ... }const i = integer(1)

if (typeof i === 'number') {

foo(i) // <= Flow doesn't complain

}