What it is
fp-ts brings typed functional programming to TypeScript, with Option, Either, Task and the type-class hierarchy those structures come from.
Model absence with Option, failure with Either, and asynchrony with Task and TaskEither. The pipe function composes small total functions into larger ones.
Installation
npm install fp-tsGetting started
The smallest useful thing you can do with it, and what each part means.
import { pipe } from 'fp-ts/function';
import * as O from 'fp-ts/Option';
import * as E from 'fp-ts/Either';
const parseAge = (input: string): E.Either<string, number> => {
const n = Number(input);
return Number.isNaN(n) ? E.left('not a number') : E.right(n);
};
const result = pipe(
parseAge('42'),
E.map((age) => age + 1),
E.chain((age) => (age >= 18 ? E.right(age) : E.left('too young'))),
E.getOrElse(() => 0),
);
// Option for values that may be absent.
const head = <A>(as: A[]): O.Option<A> =>
as.length ? O.some(as[0]) : O.none;Advanced usage
Where the library earns its place over a simpler alternative.
import * as TE from 'fp-ts/TaskEither';
const fetchBook = (id: string): TE.TaskEither<Error, Book> =>
TE.tryCatch(
() => fetch(`/api/books/${id}`).then((r) => r.json()),
(reason) => new Error(String(reason)),
);
const program = pipe(
fetchBook('42'),
TE.chain((book) => book.available ? TE.right(book) : TE.left(new Error('unavailable'))),
TE.fold(
(error) => T.of(`failed: ${error.message}`),
(book) => T.of(`got ${book.title}`),
),
);
const message = await program(); // a Task is a lazy promise — call it to runErrors and fixes
The failures you are most likely to hit, and what actually resolves them.
- Nothing runs
- A Task or TaskEither was never called. They are functions; append () to execute.
- Type errors deep inside pipe
- pipe reports at the first mismatched step. Split the chain into named intermediates to see which one.
Best practices
- Consider Effect for new projects; fp-ts is stable but its momentum has moved there.
- Import modules with a namespace alias (import * as E) — the docs assume it.
- Introduce it in one layer rather than across a whole codebase.
- Remember Task is lazy: it must be invoked to produce a promise.
Background
Why it exists, and what it was reacting to.
Created by Giulio Canti, fp-ts introduced Haskell and Scala idioms to TypeScript. It is powerful and uncompromising; much of its community has since moved toward Effect, which offers similar guarantees with a gentler surface.
