TypeScript Monads
typescript-monads is a JavaScript/TypeScript library that provides common functional programming monads and abstractions to manage control flow and state, aiming to enable cleaner, safer code by reducing null/undefined checks and explicit error handling. It currently stands at version 9.5.0, with minor and patch releases occurring frequently (monthly to quarterly), and major versions released approximately yearly (v9.0.0 in January 2024). Key differentiators include its comprehensive set of monads like Maybe, List, Either, Result, State, and Reader, offering alternatives to traditional imperative logic for handling optional values, collections, error propagation, and side effects in a more declarative and type-safe manner within TypeScript projects. The library emphasizes lazy evaluation for collections and provides robust type definitions for seamless integration into TypeScript applications.
Common errors
-
TypeError: (0, typescript_monads_1.maybeToObservable) is not a function
cause The `rxjs` package is not installed, but a function that integrates with RxJS (like `maybeToObservable`) is being called.fixInstall RxJS as a dependency: `npm install rxjs` or `yarn add rxjs`. -
TS2305: Module ''typescript-monads'' has no exported member 'result'.
cause Attempting to import `result` (lowercase) as a named export, but the primary factory functions are `ok` and `fail`, and the type is `Result` (uppercase).fixUse `import { ok, fail, Result } from 'typescript-monads'` instead. Factory functions are `ok()` and `fail()`, and the type is `Result<T, E>`. -
ReferenceError: typescriptMonads is not defined (in browser)
cause The `typescript-monads` script was loaded directly in the browser via `unpkg` without ensuring it exposes a global variable, or it's trying to access `typescriptMonads` before the script fully loads or in a context where it's not exposed.fixEnsure the script is loaded and executed, and the correct global variable name is used. As per documentation, it exposes `typescriptMonads`. If using modern bundlers, prefer `import` statements. -
TS2339: Property 'value' does not exist on type 'Maybe<number>' (or 'Result<string, Error>').
cause Directly trying to access a property like `.value` on a monad instance, which is an incorrect pattern as monads wrap values and require specific methods for safe extraction.fixUse the provided monadic methods for value extraction or transformation, such as `.valueOr(defaultValue)`, `.match(...)`, `.tapSome(...)`, `.map(...)`, or `.flatMap(...)`.
Warnings
- breaking The signature of `Maybe.apply` method was changed in v9.0.0. Code using the `apply` method might need adjustments.
- gotcha Integration with RxJS features like `maybeToObservable` requires the `rxjs` package to be explicitly installed as a dependency in your project. It is not bundled with `typescript-monads`.
- gotcha Attempting to access a value from a `None` (for Maybe) or a `Fail` (for Result) directly without using safe methods like `valueOr`, `match`, or `tapSome`/`tapOk` will result in `undefined` or an unhandled value, negating the benefits of the monad.
- gotcha The library heavily relies on TypeScript type inference. While it provides runtime safety, incorrect type declarations when creating monads or using `flatMap`/`map` can lead to type mismatches that TypeScript might catch but could lead to logical errors if types are asserted incorrectly.
Install
-
npm install typescript-monads -
yarn add typescript-monads -
pnpm add typescript-monads
Imports
- maybe
const { maybe } = require('typescript-monads')import { maybe } from 'typescript-monads' - List
import List from 'typescript-monads'
import { List } from 'typescript-monads' - Result
import { result } from 'typescript-monads'import { Result } from 'typescript-monads' - maybeToObservable
import { maybeToObservable } from 'typescript-monads'
Quickstart
import { maybe, none, List, ok, fail } from 'typescript-monads';
// Demonstrating Maybe monad for optional values
function getUserDisplayName(user: { firstName?: string, lastName?: string }): string {
return maybe(user.firstName)
.flatMap(first => maybe(user.lastName).map(last => `${first} ${last}`))
.valueOr('Guest');
}
console.log(getUserDisplayName({ firstName: 'Alice', lastName: 'Smith' }));
console.log(getUserDisplayName({ firstName: 'Bob' }));
console.log(getUserDisplayName({}));
// Demonstrating Result monad for error handling
function divide(a: number, b: number): typeof Result<number, string> {
if (b === 0) {
return fail('Cannot divide by zero');
}
return ok(a / b);
}
divide(10, 2).match({
ok: val => console.log(`Result: ${val}`), // Result: 5
fail: err => console.error(`Error: ${err}`)
});
divide(10, 0).match({
ok: val => console.log(`Result: ${val}`),
fail: err => console.error(`Error: ${err}`)
}); // Error: Cannot divide by zero
// Demonstrating List monad for functional collections
const numbers = List.of(1, 2, 3, 4, 5);
const doubledEvens = numbers
.filter(n => n % 2 === 0)
.map(n => n * 2)
.toArray();
console.log(doubledEvens); // [4, 8]