Purify TS: Functional Programming Standard Library
Purify is a TypeScript library providing a standard set of functional programming abstractions, primarily focused on Algebraic Data Types (ADTs) such as Maybe, Either, and their asynchronous counterparts. Its purpose is to enable developers to write safer, more maintainable code by handling optional values and errors explicitly. The current stable version is 2.1.4, with patch releases addressing build issues and minor fixes occurring frequently, while minor versions introduce new features. Purify distinguishes itself by offering a developer-friendly API that prioritizes practical application and ease of use over more complex theoretical constructs like Higher-Kinded Types, a deliberate design choice given TypeScript's current capabilities. It is fully type-safe, entirely written in TypeScript, and adheres to the Fantasy Land specification, aiming to seamlessly integrate robust error and optionality handling into existing TypeScript projects without requiring elaborate type definitions or runtime workarounds.
Common errors
-
Module not found: Can't resolve 'purify-ts'
cause This often occurs when using one of the Purify-TS versions known to have a broken ESM build (e.g., 2.0.2, 2.1.1, 2.1.3) or when a bundler struggles with the module resolution after the v2.0.0 build changes.fixCheck your `purify-ts` version. If it's one of the problematic ones, update to the latest stable patch (e.g., `npm install purify-ts@latest`). Ensure your `tsconfig.json` `moduleResolution` is set appropriately for ESM (`bundler` or `node16`). -
TypeError: __webpack_require__(...).Maybe is not a function
cause This error typically indicates an attempt to `require` Purify-TS in a CommonJS context when it's expecting an ESM import, or a mixing of module systems that Webpack (or another bundler) cannot reconcile with the library's ES module output.fixFor modern TypeScript projects, always use `import { Maybe } from 'purify-ts';` instead of `const Maybe = require('purify-ts').Maybe;`. Ensure your project's `package.json` has `"type": "module"` if you intend to use ESM natively, or configure your bundler to correctly handle ES module imports. -
Property 'map' does not exist on type 'unknown' or 'any'
cause This can happen if you're not correctly handling the `Maybe` or `Either` types, perhaps extracting values too early, or if TypeScript's type inference is failing due to incorrect usage, leading to the value being treated as `unknown` or `any`.fixEnsure you are using `map`, `chain`, `ap`, or other combinators on the `Maybe` or `Either` instance itself, rather than attempting to access properties on the extracted value before it's guaranteed to be present (e.g., after `getOrElse`). Verify your TypeScript version is compatible with Purify-TS.
Warnings
- breaking Version 2.0.0 introduced significant breaking changes, including changing the default TypeScript compile target from `es5` to `es2015`. It also removed the legacy `/es` build output, replacing it with a new `/esm` output. Projects relying on older compile targets or specific build paths will need to adjust their configurations.
- gotcha Multiple recent versions (2.1.3, 2.1.1, 2.0.2) have shipped with broken ESM builds, explicitly marked as 'DO NOT USE' in release notes. This can lead to module resolution errors or runtime issues in ESM projects.
- gotcha While Purify-TS is written with TypeScript and type safety in mind, explicit support for newer TypeScript versions (e.g., TS 5.5 support in v2.1.0) implies potential type-related compatibility issues with significantly older TypeScript compiler versions.
- gotcha Pre-v2.0.0, the structure and export patterns might differ, particularly for async ADTs like `MaybeAsync` and `EitherAsync`. Issues like incorrect type definitions for `ap` and `extend` were fixed in older versions (e.g., v1.3.5).
Install
-
npm install purify-ts -
yarn add purify-ts -
pnpm add purify-ts
Imports
- Maybe
const Maybe = require('purify-ts').Maybeimport { Maybe, Just, Nothing } from 'purify-ts' - Either
import Either from 'purify-ts/Either'
import { Either, Left, Right } from 'purify-ts' - MaybeAsync
import { MaybeAsyncTypeRef } from 'purify-ts'import { MaybeAsync } from 'purify-ts'
Quickstart
import { Maybe, Either, Just, Nothing, Left, Right } from 'purify-ts';
// Example 1: Handling optional values with Maybe
function getUserEmail(id: number): Maybe<string> {
const users = {
1: { name: 'Alice', email: 'alice@example.com' },
2: { name: 'Bob', email: undefined },
3: { name: 'Charlie', email: 'charlie@example.com' }
};
const user = users[id];
return Maybe.fromNullable(user?.email); // Safely get email, handles null/undefined
}
console.log(`User 1 email: ${getUserEmail(1).map(e => e.toUpperCase()).getOrElse('No email found')}`);
// Expected: User 1 email: ALICE@EXAMPLE.COM
console.log(`User 2 email: ${getUserEmail(2).map(e => e.toUpperCase()).getOrElse('No email found')}`);
// Expected: User 2 email: No email found
console.log(`User 4 email: ${getUserEmail(4).map(e => e.toUpperCase()).getOrElse('No email found')}`);
// Expected: User 4 email: No email found
// Example 2: Working with Either for success/failure
function divide(a: number, b: number): Either<string, number> {
return b === 0 ? Left('Cannot divide by zero') : Right(a / b);
}
const result1 = divide(10, 2);
result1.ifRight(val => console.log(`10 / 2 = ${val}`)); // Expected: 10 / 2 = 5
result1.ifLeft(err => console.error(`Error: ${err}`));
const result2 = divide(10, 0);
result2.ifRight(val => console.log(`10 / 0 = ${val}`));
result2.ifLeft(err => console.error(`Error: ${err}`)); // Expected: Error: Cannot divide by zero
// Example 3: Using fromPredicate with type guards
const isPositive = (num: number): num is number => num > 0;
const maybePositive = Maybe.fromPredicate(isPositive, 5);
console.log(`Is 5 positive? ${maybePositive.isJust()}`); // Expected: Is 5 positive? true
const maybeNegative = Maybe.fromPredicate(isPositive, -1);
console.log(`Is -1 positive? ${maybeNegative.isJust()}`); // Expected: Is -1 positive? false