@urbandoor/optional v2.0.0
optional
optional is a TypeScript library for dealing with empty values. JavaScript has
two empty values, null and undefined, and since TypeScript 2.0, saying some
type T is optional (in the broad sense of the phrase) yields three distinct
interpretations:
T | nullT | undefinedT | null | undefined
This library is designed to handle the broadest interpretation: the last one.
API
The most useful part of the API lies in the exported types. A few utility functions are also exported, but in most use cases they are not needed, because those specific user-defined type guards have been superseded by the ones built-in to the compiler.
type Empty = null | undefined:Emptydefines what we consider empty values.type Option<T> = T | Empty:Option<T>denotes typeTwith potential absence.type Lazy<T> = T | (() => T):Lazy<T>is either a value of typeTor a function that produces a value of typeT. It represents something that may or may not have been evaluated yet.isEmpty<T>(x: Option<T>): x is Empty:isEmptyis a type guard that, when true, asserts that the givenOption<T>isEmpty.isDefined<T>(x: Option<T>): x is T:isDefinedis a type guard that, when true, asserts that the givenOption<T>is in factTand not empty.getOrElse<T>(x: Option<T>, defaultValue: Lazy<T>): T:getOrElsereturns the underlying value of a givenOption<T>if it is non-empty. Otherwise it evaluatesdefaultValueusingforceand returns that instead.force<T>(x: Lazy<T>): T:forceevaluates the givenLazy<T>. If the given value is not a function, then it is treated as having been evaluated and directly returned. If it is a function, then it is invoked, and its result is returned.
Partial deprecation
When handling an Option<T> where T is not boolean, we actively discourage
using isEmpty, isDefined, and getOrElse. All three of these things have
analogous native JavaScript constructs that are considered more idiomatic:
isEmpty(x)can be written as!xin any boolean expression or context.isDefined(x)can be written asxor!!xin any boolean expression or context.getOrElse(x, () => v)can be written asx || vin any expression.
TypeScript, since 2.0, has the native ability to perform type narrowing for the idiomatic constructs to work in a type-safe manner.
However, when dealing with Option<boolean>, one is presented with four
possibilities: true, false, null, undefined. If all three falsy values are
considered equivalent, then the idiomatic constructs remain safe to use, but if
false is considered distinct from null or undefined, then the situation calls
for using these utility functions.
Rationale
This library does not handle T | null because it is a short-enough spelled type
such that it does not warrant its own type alias. It also does not handle
T | undefined, because in situations where that matters, such as argument or
property declaration, x?: T already implies x?: T | undefined. For a short
period of time, a bleeding edge version of TypeScript actually supported the type
syntax T?, but through an extended discourse it became obvious that there is no
single consensus as to what T? ought to mean. This library is formulated for
situations where one might see T? as T | null | undefined.
History
Before TypeScript 2.0, the language did not expose null and undefined as
distinct types, so this library defined type Option<T> = T | void. Unioning with
void was an effective way to "poison" a type T so that one cannot safely access
a property without using one of the type guards. With the advent of strict null
checking support as well as distinct null and undefined types in TypeScript 2.0,
this library's role has receded into a more documentary one.
9 years ago