ts-results: Rust-like Result and Option for TypeScript
ts-results is a TypeScript library that provides compile-time error checking and optional values, inspired by Rust's `Result` and `Option` enums. It offers a type-safe approach to handling fallible operations and potential absence of values without relying on exceptions or `null`/`undefined`. The current stable version, 3.3.0, was last published in 2021. This package differentiates itself by offering a lightweight, focused implementation of these core algebraic data types, in contrast to more comprehensive functional programming libraries. It enables developers to explicitly model success (`Ok`) or failure (`Err`) states and presence (`Some`) or absence (`None`) of a value, leveraging TypeScript's type system to ensure these states are handled at compile time, thereby reducing runtime errors.
Common errors
-
Cannot use import statement outside a module
cause The `ts-results` package (v3.3.0) is primarily configured as CommonJS. When a project is set up for ES Modules (`"type": "module"` in `package.json` or `.mjs` files), direct `import` statements for CJS packages can cause this error.fix1. If your project *must* be ESM, consider migrating to the `ts-results-es` fork which has explicit ESM support. 2. If staying with `ts-results`, ensure your build process (e.g., Webpack, Rollup) or TypeScript configuration (`tsconfig.json`) correctly handles CommonJS interoperability, or configure your output module system to CommonJS (`"module": "CommonJS"`). 3. For Node.js, you might need to use `require()` if not using a bundler, but this can lead to type issues. -
Property 'val' does not exist on type 'Err<E>'
cause This error occurs when attempting to access `result.val` directly on an `Err` variant of a `Result<T, E>` without a preceding type guard (e.g., `if (result.ok)`), or attempting to access `result.val` on an `Ok` variant when `val` refers to the error type.fixAlways use a type guard to narrow the `Result` type before accessing its value. For an `Ok` result, check `if (result.ok)` then `result.val` will be `T`. For an `Err` result, check `if (!result.ok)` (or `if (result.err)` if using the fork), then `result.val` will be `E`.
Warnings
- gotcha The original `ts-results` package (v3.3.0) primarily targets CommonJS modules. Using it in native ES Module environments without proper transpilation (e.g., via a bundler) may lead to import errors like 'Cannot use import statement outside a module' or 'require is not defined'.
- deprecated The original `ts-results` package has not been updated since 2021 (v3.3.0). It is effectively in a maintenance state with no active development. Users seeking new features, bug fixes, or dedicated ESM support should consider the `ts-results-es` fork, which is actively maintained.
- breaking If migrating from `ts-results` to the `ts-results-es` fork, be aware of API breaking changes. Direct property access like `result.val` (for `Ok` and `Some` values) and `result.val` (for `Err` errors) have been replaced by `.value` or `.error` respectively. Additionally, boolean flags like `result.ok`, `result.err`, `option.some`, `option.none` are replaced by methods such as `result.isOk()`, `result.isErr()`, `option.isSome()`, `option.isNone()`.
Install
-
npm install ts-results -
yarn add ts-results -
pnpm add ts-results
Imports
- Result
const Result = require('ts-results').Resultimport { Result } from 'ts-results' - Ok
import { ok } from 'ts-results' // Case sensitiveimport { Ok } from 'ts-results' - Err
import { error } from 'ts-results' // Incorrect name for the error variantimport { Err } from 'ts-results' - Option
import { option } from 'ts-results'import { Option } from 'ts-results' - Some
import { Value } from 'ts-results' // Not a part of the APIimport { Some } from 'ts-results' - None
import { None } from 'ts-results'
Quickstart
import { Ok, Err, Result, Some, None, Option } from 'ts-results';
import { readFileSync, existsSync } from 'fs';
// --- Result Example: Handling file operations ---
function readFileSafe(path: string): Result<string, 'file_not_found' | 'read_error'> {
if (!existsSync(path)) {
return new Err('file_not_found');
}
try {
const content = readFileSync(path, 'utf8');
return new Ok(content);
} catch (e) {
return new Err('read_error');
}
}
const filePath = 'test.txt';
// Simulate creating a file for the example
require('fs').writeFileSync(filePath, 'Hello, ts-results world!');
const fileResult = readFileSafe(filePath);
if (fileResult.ok) {
console.log(`File content: ${fileResult.val}`);
} else {
console.error(`Error reading file: ${fileResult.val}`);
}
// --- Option Example: Handling potentially missing configuration ---
interface UserConfig {
theme?: string;
notificationsEnabled?: boolean;
}
function getUserTheme(config: UserConfig): Option<string> {
if (config.theme) {
return new Some(config.theme);
} else {
return None;
}
}
const userSettings: UserConfig = { notificationsEnabled: true };
const themeOption = getUserTheme(userSettings);
if (themeOption.some) {
console.log(`User theme: ${themeOption.val}`);
} else {
console.log('User theme not set, using default.');
}
// Clean up simulated file
require('fs').unlinkSync(filePath);