TypeScript Nullable
typescript-nullable is a utility library for TypeScript that formalizes the concept of possibly absent values, providing a type-safe and functional approach to handling `null` and `undefined`. It defines a `Nullable<T>` type, which is explicitly `T | null | undefined`, mirroring the `Maybe` type found in functional languages like Haskell or Elm. Beyond the type definition, the library exports a `Nullable` object containing a suite of utility functions designed to interact safely with these potentially absent values. These functions, such as `map`, `withDefault`, `isNone`, and `isSome`, are curried and pure, promoting a functional programming style and enhancing type safety by leveraging TypeScript's type guards. As of version 0.6.0, the library is actively maintained, with incremental updates focusing on API refinements and feature additions, though a specific release cadence is not formally published. Its core value proposition lies in enabling developers to write more resilient code by explicitly managing the presence or absence of values, thereby reducing runtime errors associated with unexpected `null` or `undefined` references and offering a robust alternative to imperative null checks.
Common errors
-
TS2305: Module '"typescript-nullable"' has no exported member 'isSome'.
cause Attempting to import utility functions (like `isSome`, `map`, `withDefault`) directly from the package.fixUtility functions are properties of the `Nullable` object. Import `Nullable` and then access the functions: `import { Nullable } from 'typescript-nullable'; Nullable.isSome(value);` -
TypeError: Nullable.map is not a function
cause This usually happens when `Nullable` is not imported correctly, or when using CommonJS `require` without properly handling the ES module interop, leading to `Nullable` being `undefined` or an unexpected object.fixEnsure you are using `import { Nullable } from 'typescript-nullable';` for ESM environments. If in CommonJS, try `const { Nullable } = require('typescript-nullable');` or `const Nullable = require('typescript-nullable').Nullable;` -
TS2305: Module '"typescript-nullable"' has no exported member 'None'.
cause Attempting to import the `None` type alias directly from the package.fixThe `None` type is an internal alias for `null | undefined` and is not exported. You should use `null | undefined` directly or define your own `type None = null | undefined;` if you wish to use that alias in your code.
Warnings
- breaking The library is currently in version 0.x.x, indicating that the API might not be stable. Expect potential breaking changes in minor or even patch releases before a 1.0.0 release.
- gotcha All utility functions are curried, meaning they can be called with arguments one at a time, returning new functions until all arguments are satisfied. Forgetting this pattern can lead to unexpected `TypeError`s.
- gotcha The `Nullable` symbol is used for both the `Nullable<T>` type and the `Nullable` object containing utility functions. While convenient in TypeScript, it can be confusing if you expect them to be separate imports or if migrating from languages with distinct type/value namespaces.
Install
-
npm install typescript-nullable -
yarn add typescript-nullable -
pnpm add typescript-nullable
Imports
- Nullable
import { Nullable } from 'typescript-nullable'; - Nullable object functions
import { isSome } from 'typescript-nullable';import { Nullable } from 'typescript-nullable'; Nullable.isSome(value); - None
import { None } from 'typescript-nullable';type None = null | undefined;
Quickstart
import { Nullable } from 'typescript-nullable';
// Demonstrate Nullable type definition implicitly
type UserName = Nullable<string>;
const userName1: UserName = 'Alice';
const userName2: UserName = null;
const userName3: UserName = undefined;
console.log(`User Name 1: ${userName1}`);
console.log(`User Name 2: ${userName2}`);
console.log(`User Name 3: ${userName3}`);
// Demonstrate utility functions
const toUpperCase = (text: string) => text.toUpperCase();
// Nullable.map - example with curried usage
const mappedName1 = Nullable.map(toUpperCase)(userName1);
const mappedName2 = Nullable.map(toUpperCase)(userName2);
console.log(`Mapped Name 1: ${mappedName1}`);
console.log(`Mapped Name 2: ${mappedName2}`);
// Nullable.withDefault
const displayName1 = Nullable.withDefault('Guest')(userName1);
const displayName2 = Nullable.withDefault('Guest')(userName2);
console.log(`Display Name 1: ${displayName1}`);
console.log(`Display Name 2: ${displayName2}`);
// Nullable.isSome and Nullable.isNone with TypeScript type guards
const potentiallyNullString: Nullable<string> = Math.random() > 0.5 ? 'Hello' : null;
if (Nullable.isSome(potentiallyNullString)) {
console.log(`Value is present: ${potentiallyNullString.length}`); // TS knows it's a string here
} else {
console.log('Value is absent.'); // TS knows it's null | undefined here
}
// Explicit currying example
const mapToUpper = Nullable.map(toUpperCase);
console.log(`Curried map result: ${mapToUpper('world')}`);
console.log(`Curried map result (null): ${mapToUpper(null)}`);