Functional programming isn’t just for Haskell or Scala anymore — with TypeScript’s evolving type system, we can now model powerful concepts like safe error handling, optional values, and composable results in a clean, type-safe way.
ts-fp-utils is a small but expressive functional programming library for TypeScript that brings together the best of Rust’s Result and Option with Go’s error semantics.
👉 NPM Package:
npm install ts-fp-utils
💡 Why Another FP Library?
Most TypeScript codebases handle errors using plain try/catch blocks, which can get messy and unpredictable — especially in async-heavy systems or service layers.
This library was built to solve three real-world problems:
- Make error handling composable and predictable
- Avoid null checks everywhere with an expressive Option type
- Introduce domain-specific failures inspired by Go and DDD
In short: make failure a first-class citizen of your TypeScript code.
⚙️ The Core Modules
| File | Role |
|---|---|
pair.ts |
Simple immutable key–value pair |
option.ts |
Optional value wrapper (Rust’s Option) |
result.ts |
Success/error container (Rust’s Result) |
failure.ts |
Base error abstraction (Go’s error) |
failures.ts |
Helper methods for working with Failure
|
ApiFailure.ts, EntityNotFound.ts, etc. |
Domain-level exception classes |
🧠 Option — Safe Optional Values
Rust’s Option<T> inspired our implementation.
It lets you express “value or none” semantics clearly.
import { Option } from "ts-fp-utils";
const userId = Option.orUndefinedOrNullable(null);
console.log(userId.isEmpty()); // true
const fallback = userId.getOrElse("guest");
console.log(fallback); // guest
const name = Option.ok("John Doe")
.map(n => n.toUpperCase())
.get();
console.log(name); // JOHN DOE
Async? No problem 👇
const asyncOpt = await Option.of(async () => "Hello Async");
console.log(asyncOpt.get()); // Hello Async
🧩 Composing with Result
The Result<T, E> type (inspired by Rust) ties it all together — representing success or failure outcomes without throwing exceptions.
import { Result } from "ts-fp-utils";
function divide(a: number, b: number): Result<number, Failure> {
if (b === 0) return Result.err(new IllegalArgument("Division by zero"));
return Result.ok(a / b);
}
const result = divide(10, 0);
if (result.isErr()) console.error(result.unwrapErr().message);
💥 Failure — When Things Go Wrong
Inspired by Go’s error and Kotlin’s Result.Failure, this class makes failures explicitly representable and composable.
import { Failure } from "ts-fp-utils";
try {
const op = Failure.of(() => {
throw new Error("Network timeout!");
});
op.ifPresent(f => console.error("Failure occurred:", f.message));
} catch (err) {
console.error("Unexpected exception:", err);
}
Wrapping causes
const wrapped = Failure.wrap("Outer context", new TypeError("Invalid response"));
console.log(wrapped.toString());
// Failure{message='Outer context', cause=TypeError('Invalid response')}
🧰 Failure Helpers
Helper methods to make common failure patterns easier.
import {
emptyFailure,
failureWithMessage,
wrapFailure,
wrapExistingFailure
} from "ts-fp-utils";
const fail = failureWithMessage("Validation failed");
console.log(fail.isPresent()); // true
const wrapped = wrapFailure("Database insert failed", new Error("Unique constraint violated"));
console.log(wrapped.message); // Database insert failed
🚨 Domain-Specific Failures
You can define meaningful, domain-aware failures — no more vague throw new Error().
import {
EntityNotFound,
EntityAlreadyExists,
AuthFailure,
OperationNotAllowed
} from "ts-fp-utils";
function findUser(id: string) {
if (!id) throw new AuthFailure("User not authenticated.");
throw new EntityNotFound(`User with ID ${id} not found.`);
}
try {
findUser("123");
} catch (err) {
if (err instanceof EntityNotFound) console.error("Not Found:", err.message);
}
Built-in failure types include:
ApiFailureAuthFailureEntityAlreadyExistsEntityNotFoundEntityValidationFailedIllegalArgumentIllegalStateInternalFailureInvalidRequestOperationNotAllowed
Each extends the base Failure class and adds clear semantic meaning to your application’s flow.
🧱 Built for Real Systems
This library was designed for use in backend applications, domain-driven systems, and API layers where:
- You need robust error propagation
- You prefer explicit control flow over exceptions
- You want Go-like simplicity with Rust-like safety
🚀 Try It Out
npm install ts-fp-utils
Then start composing safe, expressive TypeScript today.
No more surprise undefined, no more hidden exceptions.
💬 Final Thoughts
By combining Rust’s Result & Option patterns with Go’s pragmatic error philosophy, we can bring clarity and safety to everyday TypeScript.
If you’re tired of fighting try/catch, or your codebase is full of null guards — give this library a spin.
GitHub: github.com/veerakumarak/ts-fp-utils
NPM: npmjs.com/package/ts-fp-utils
🧡 Happy functional programming in TypeScript!
Top comments (0)