Overview
fp-ts is a TypeScript library designed to facilitate the adoption of functional programming paradigms within TypeScript projects. It provides a collection of immutable data structures and higher-order functions that enable developers to write code that is declarative, composable, and side-effect free. The library draws inspiration from functional languages like Haskell and Scala, incorporating concepts such as functors, applicatives, monads, and optics (fp-ts modules documentation).
The core philosophy behind fp-ts is to leverage TypeScript's advanced type system to enforce functional programming principles at compile time. This approach aims to reduce runtime errors and improve the predictability of application behavior. By promoting pure functions and immutable data, fp-ts helps manage complexity in larger codebases, making them easier to test and reason about. For instance, the use of Option and Either types encourages explicit handling of potential null or undefined values and error states, respectively, rather than relying on runtime checks.
fp-ts is particularly well-suited for developers who are already familiar with functional programming concepts or those looking to deepen their understanding and application of these patterns in a TypeScript environment. Its rigorous type definitions can act as a learning tool, guiding users toward more robust and idiomatic functional code. While the initial learning curve can be steep due to the abstract nature of some category theory concepts, the long-term benefits include enhanced code quality, maintainability, and a more robust application architecture.
The library is entirely free and open-source, making it accessible for individual developers and large organizations alike. It integrates well with existing TypeScript projects, providing tools that can be adopted incrementally rather than requiring a complete architectural overhaul. This allows teams to introduce functional patterns strategically where they offer the most significant benefits, such as in data processing pipelines, state management, or complex business logic. Unlike some utility libraries that offer functional-style helpers, fp-ts commits deeply to mathematical rigor and type safety, distinguishing it for applications where correctness and predictability are paramount.
Key features
- Type-safe functional constructs: Offers types like
Optionfor nullable values,Eitherfor error handling, andTaskEitherfor asynchronous operations with error handling, promoting explicit handling of side effects and potential failures. - Immutable data structures: Provides utilities for working with immutable arrays, records, and other data types, which helps prevent unintended mutations and makes state management more predictable.
- Higher-order functions: Includes a wide array of functions that operate on other functions, enabling powerful composition and abstraction, such as
pipefor function chaining and various combinators. - Category theory concepts: Implements type classes like
Functor,Applicative,Monad, andFoldable, allowing developers to apply abstract mathematical concepts to practical programming problems (fp-ts module documentation). - Optics (Lenses, Prisms, Traversals): Provides tools for immutable access and modification of deeply nested data structures, simplifying complex data transformations.
- Concurrency and parallelism utilities: Includes types like
TaskandIOfor managing asynchronous and synchronous side effects in a pure functional manner. - Extensive module ecosystem: Organized into numerous modules, allowing developers to import only the necessary parts, which helps keep bundle sizes optimized.
Pricing
fp-ts is distributed under an open-source license, making it entirely free to use for any purpose, including commercial applications. There are no paid tiers or premium features.
| Tier | Cost | Description | As of Date |
|---|---|---|---|
| Core Library | Free | Access to all fp-ts modules, types, and functions. Includes ongoing maintenance and community support. | 2026-05-27 |
Common integrations
- React/Redux: fp-ts can be used to manage application state and side effects in React applications, often in conjunction with libraries like
redux-observableor custom hooks that leverageTaskEither. - Node.js APIs: Ideal for building robust and error-resistant backend services where explicit error handling and predictable behavior are critical.
- Data processing pipelines: Its immutable data structures and composable functions make it suitable for transforming and processing data streams reliably.
- Other TypeScript projects: Easily integrates into any TypeScript codebase that aims to adopt functional programming principles, enhancing type safety and code clarity.
Alternatives
- Effect-TS: A TypeScript library that focuses on type-safe, purely functional effects for managing complexity in applications, offering strong similarities to Scala's ZIO (Effect-TS homepage).
- Ramda: A practical functional JavaScript library designed for a functional programming style, emphasizing immutability and automatic currying. It does not enforce TypeScript's type system to the same extent as fp-ts.
- Lodash/fp: A functional programming style variant of the popular Lodash utility library, providing immutable, auto-curried, iteratee-first, data-last methods (Lodash/fp documentation).
Getting started
To begin using fp-ts, first install it via npm or yarn:
npm install fp-ts
# or
yarn add fp-ts
Here's a basic example demonstrating the use of Option for safe handling of potentially missing values and pipe for function composition:
import { pipe } from 'fp-ts/function';
import * as O from 'fp-ts/Option';
interface User {
id: number;
name: string;
email?: string; // Optional email
}
const findUserById = (id: number): O.Option<User> => {
const users: User[] = [
{ id: 1, name: 'Alice', email: '[email protected]' },
{ id: 2, name: 'Bob' }, // No email
];
const user = users.find(u => u.id === id);
return O.fromNullable(user); // Lifts a nullable User into an Option<User>
};
const getUserEmail = (user: User): O.Option<string> => {
return O.fromNullable(user.email);
};
// Example 1: User with email
const result1 = pipe(
findUserById(1), // Returns Option<User>
O.chain(getUserEmail), // If Some(User), gets Option<string> (email)
O.match(
() => 'Email not found for user 1',
email => `User 1 email: ${email}`
)
);
console.log(result1); // Expected: "User 1 email: [email protected]"
// Example 2: User without email
const result2 = pipe(
findUserById(2),
O.chain(getUserEmail),
O.match(
() => 'Email not found for user 2',
email => `User 2 email: ${email}`
)
);
console.log(result2); // Expected: "Email not found for user 2"
// Example 3: User not found
const result3 = pipe(
findUserById(99),
O.chain(getUserEmail),
O.match(
() => 'User 99 not found',
email => `User 99 email: ${email}`
)
);
console.log(result3); // Expected: "User 99 not found"
This example demonstrates how Option allows you to chain operations safely, avoiding null-pointer exceptions by explicitly handling the presence or absence of a value. The pipe function provides a readable way to compose multiple functional operations.