TypeScript Nullable
What is This?
Glad you asked. This is a type-safe formalization of the concept of possibly absent values in TypeScript. It is perhaps even more importantly a module of type-safe utility functions that deal with possibly absent values.
Think of it roughly like a JavaScript-friendly version of Haskell’s or Elm’s Maybe
type and corresponding module of functions for dealing with Maybe
. It is functional to its core, with typed and curried pure functions.
Installation
From the command line:
$ npm install --save typescript-nullable
In your TypeScript files:
import { Nullable } from 'typescript-nullable'
Nullable Type Definition
type None = null | undefined type Nullable<T> = T | None
Module Utility Functions
This module also ships with a Nullable
object that contains multiple useful functions for dealing with potentially absent values. Thus we have both a Nullable
type and a Nullable
object of utility functions.
All utility functions are curried to the extent that their final argument is optional. If a final argument is not provided, the function will return another function that expects that final argument.
Nullable.isNone
Determines if a provided Nullable
is None
and provides a type guard.
Type Annotation
<T>(nullable: Nullable<T>): nullable is None
Example Usage
Nullable.isNone('noob noob') // falseNullable.isNone(null) // trueNullable.isNone(undefined) // true const possiblyNullValue: Nullable<string> = 'noob noob' if (Nullable.isNone(possiblyNullValue)) { // in this scope, TypeScript knows possiblyNullValue is a None}
Nullable.isSome
Determines if a provided Nullable
is a concrete value and provides a type guard.
Type Annotation
<T>(nullable: Nullable<T>): nullable is T
Example Usage
Nullable.isNone('noob noob') // trueNullable.isNone(null) // falseNullable.isNone(undefined) // false const possiblyNullValue: Nullable<string> = 'noob noob' if (Nullable.isSome(possiblyNullValue)) { // in this scope, TypeScript knows possiblyNullValue is a concrete string}
Nullable.map
Applies the provided function to the provided Nullable
only if it is not None
. Returns null
otherwise.
Type Annotation
<A, B>(func: (val: A) => B): (nullable: Nullable<A>) => Nullable<B>
Example Usage
const toUpper = (text: string): string => text.toUpperCase()Nullable.map(toUpper, 'noob noob') // NOOB NOOBNullable.map(toUpper, null) // nullNullable.map(toUpper, undefined) // null
Nullable.withDefault
Provided a default value and a Nullable
, will return the default value when the Nullable
is None
. Will return the concrete value of the Nullable
if it is, in fact, concrete.
Type Annotation
<T>(defaultVal: T): (nullable: Nullable<T>) => T
Example Usage
Nullable.withDefault('morty')('rick') // 'rick'Nullable.withDefault('morty')(null) // 'morty'
Nullable.maybe
Provided a default value, a function, and a Nullable
, will return the default value when the Nullable
is None
. Will return the provided function applied to the concrete value of the Nullable
if it is, in fact, concrete.
Type Annotation
<A, B>(defaultVal: B, f: (a: A) => B): (nullable: Nullable<A>) => B
Example Usage
import { add } from 'ramda'Nullable.maybe(7, add(83), null)) // 7Nullable.maybe(7, add(83), 34)) // 117
Nullable.andThen
Used for chaining functions that take a raw value of type T
but return a Nullable<T>
. This is like Haskell's bind
or >>=
.
Type Annotation
Nullable.andThen<A, B>(func: (val: A) => Nullable<B>): (nullable: Nullable<A>) => Nullable<B>
Example Usage
import { compose, curry } from 'ramda' // Some arbitrary function that returns a Nullable:const safeDivide = curry((a: number, b: number): Nullable<number> => { return a === 0 ? null : b / a}) compose( Nullable.andThen(safeDivide(3)), Nullable.andThen(safeDivide(0)), // this line results in a None value so the rest of the composition chain passes along None without blowing up or throwing an exception Nullable.andThen(safeDivide(4)), safeDivide(2),)(32) // null compose( Nullable.andThen(safeDivide(3)), Nullable.andThen(safeDivide(5)), Nullable.andThen(safeDivide(4)), safeDivide(2),)(32) // 0.5333333333333333
Nullable.ap
Used for writing in the applicative style. For "lifting" functions into the Nullable
context.
Type Annotation
Nullable.ap<A, B>(targetNullable: Nullable<A>): (applicativeNullable: Nullable<(val: A) => B>) => Nullable<B>
Example Usage
// Some arbitrary curried function that takes 3 concrete values:const addThreeNumbers = (a: number) => (b: number) => (c: number) => a + b + c compose( Nullable.ap(3), Nullable.ap(2), Nullable.ap(1),)(addThreeNumbers) // 6 // This can be thought of as "lifting" addThreeNumbers into the context of its passed-in arguments being Nullable:compose( Nullable.ap(3), Nullable.ap(null as Nullable<number>), // note we have to typecast this here because TypeScript can’t be sure what kind of Nullable<T> it has at this point. Nullable.ap(1),)(addThreeNumbers) // null