# fp-ts: typed functional programming for TypeScript, and what its merger means

> The library that made algebraic data types a normal thing to see in a TypeScript codebase is being absorbed into the Effect-TS ecosystem, which changes the answer to whether you should start a new project on it.

**gcanti/fp-ts** — Functional programming in TypeScript

- Repository: https://github.com/gcanti/fp-ts
- Website: https://gcanti.github.io/fp-ts/
- Stars: 11,551 · Forks: 509
- Language: TypeScript
- License: MIT
- Published: 2026-10-06 · Updated: 2026-10-06 · Language: en
- Canonical page: https://hysenlabs.com/projects/gcanti-fp-ts

## What fp-ts actually is, in the project's own words

The repository describes the package as a library for typed functional programming in TypeScript. The stated goal is to let developers use patterns and abstractions that are available in most functional languages, and to that end it ships the popular data types and type classes: Option, Either, IO, Task, Functor, Applicative and Monad.

That list is the whole pitch. The idea is that you write pure code and compose it through higher order abstractions rather than threading error handling through return values and try blocks.

The README is explicit that the inspirations are Haskell, PureScript and Scala. That lineage shows in the module naming: everything is a data type with static constructors, and every operation comes in the same shapes. `Option` for a value that might be absent, `Either` for a computation that might fail, `IO` for a deferred side effect, `Task` for a deferred asynchronous side effect.

The stated distinctive feature is higher kinded types, which TypeScript does not support natively. That is the hard technical claim in the package and the reason the type-level machinery is as elaborate as it is. It is also what lets a single `map` signature work across all those data types.

## Installing it, and the duplicate version trap

Installation is one command:

```bash
npm install fp-ts
```

That is the entire install section. What follows it is more interesting, because it is the first operational warning a new user meets: make sure you always have a single version of fp-ts in your project. Multiple versions are known to cause `tsc` to hang during compilation, and the README suggests checking with `npm ls fp-ts`, making sure there is one version and the rest are marked `deduped`.

A compiler hang is a nasty failure mode, and it is worth understanding why it happens. Identical types from two copies of the library are structurally identical but nominally different to the type checker, so every comparison between them fails and the checker spends its time on it. Hoisting the dependency fixes the symptom.

The second installation concern is TypeScript version. The README's compatibility table maps fp-ts versions to minimum TypeScript versions: 2.0.x and later need TypeScript 3.5 or newer, 1.15.x and later need 3.1 or newer, and anything at or below 1.14.4 needs 2.8 or newer, with a note that a TypeScript older than 3.0.1 requires polyfilling the `unknown` type. The project states that it is conceived, tested and meant to be consumed with the `strict` flag turned on.

## Higher kinded types on a language that lacks them

TypeScript has generics, but it does not let you abstract over the relationship between a type and the type constructor applied to it. A `Functor` constraint in Haskell can talk about `f a` for any `f`. In TypeScript the library encodes that relationship through its own type level machinery, using type aliases with a naming convention that stands in for the kind parameter.

This is the single most important thing to understand about the library before you adopt it. Every design tradeoff downstream follows from it. You get real type class dispatch and real HKT encoding, and you pay for it in compile times and in a type signature syntax that does not look like anything else in your codebase.

The payoff is that a function written once against `Functor` works over every structure the library models. The library's naming convention for this, documented in a code conventions guide, gives functions dual names: a data-last form and a data-first form, the latter typically suffixed with a `W` when it takes a function as its first argument. Release 2.16.0 shows this in practice, adding a family of `tap` variants, each documented with an alias such as `chainFirstEitherK`, `chainFirstIOK` and `chainFirstTaskK`, alongside the more conventional `mapError` and `mapBoth`, with `mapLeft` and `bimap` given as their better known equivalents.

That naming scheme is a real ergonomic cost. You will be reading `chainFirstEitherK` on an unfamiliar line and needing a second to place it.

## The merger announcement is the most consequential thing on the README

The most important paragraph in the repository is an announcement at the top of the README, above even the introduction. It states that fp-ts is joining the Effect-TS ecosystem, that Giulio Canti, the author, is being welcomed into the Effect organization, and that Effect-TS can be regarded as the successor to fp-ts v2, effectively a v3.

The README also states plainly what this means for new users: new projects should not start on fp-ts v2. That is a direct instruction from the project itself, and it changes the usual reason people open this page.

A secondary detail worth noting is that the Effect organization is already visible in the development dependencies. `@effect/dtslint` and `@effect/language-service` appear in `package.json`, which suggests the tooling has been migrating even if the library modules have not.

For people already using fp-ts, the announcement is not a deprecation notice with a removal date. The package remains MIT licensed, installable from npm, and documented, with an API reference for the current 2.x line and a separate one for 1.x. The practical read is that 2.x is in maintenance, which is a perfectly good position for a stable codebase.

## The build and test setup behind the releases

`package.json` describes a build that compiles twice, once per module target, and then runs a build script:

```json
"build": "tsc -p ./tsconfig.build.json && tsc -p ./tsconfig.build-es6.json && ts-node scripts/build",
```

The dual compile is why `package.json` carries both a `main` field pointing at CommonJS output in `./lib/index.js` and a `module` field pointing at ES module output in `./es6/index.js`, with typings in `lib/index.d.ts`. Supporting both module systems in one package is not free, and this is where that cost shows.

The test script is a chain rather than a single runner:

```json
"test": "npm run lint && npm run prettier && npm run dtslint && npm run vitest && npm run docs",
```

So a full check runs ESLint, then Prettier in list-different mode, then dtslint for type-level tests, then Vitest, then regenerates documentation. The type-level test step is not optional decoration. For a library whose main contribution is types, dtslint is where the contribution actually lives.

There is also a `dpdm` script that runs the dependency-cruiser equivalent with an explicit circular dependency check and exit code, which matters in a package where modules import each other's type classes. `sideEffects` is set to `false`, so bundlers can tree shake it, and the package ships `perf/` and `examples/` directories, including two files named after a well-known fp-ts course series.

Published releases are small and incremental. Version 2.16.9 adds support for `strictBuiltinIteratorReturn`. Version 2.16.5 fixes a `RangeError` where the maximum call stack size was exceeded when invoking `chainWithIndex`. Version 2.16.0 is the large one, adding the `tap` family and several dual aliases.

## What the repository will not teach you, and where to go instead

The documentation section opens with a disclaimer that is unusual and completely honest: teaching functional programming is out of scope for this project, so the documentation assumes you already know what functional programming is. If you do not, this library will not teach you, and the effort of reading the API docs without that background is high.

What you get instead is a reference: generated API docs for the current 2.x line at gcanti.github.io/fp-ts, a separate reference for 1.x in that repository's own `1.x` branch, a learning resources page, and an ecosystem page listing the libraries built on top of this one.

Support goes through a Discord server and the `#fp-ts` channel on FP Slack. Development conventions are published as their own guide, which is worth reading before opening a pull request because the dual-naming scheme is enforced rather than suggested.

The repository tree also tells you what is not in the library. There is no application code, no server, no CLI. It is `src/`, `test/`, `dtslint/`, `docs/`, `perf/`, `examples/` and build configuration, which is what you would expect from a library that is deliberately narrow. The one bit of external dependency worth naming is that `fp-ts` is a library for libraries as much as for applications: the type classes assume you will build your own domain types on top of them.

## Conclusion

fp-ts did the hard part early. Higher kinded types on top of a language that has no native support for them, plus a set of type classes that behave the way they do in Haskell or PureScript, is why the package has 11551 stars and a 190 item issue tracker. What has changed is the direction of travel. The README now states that the project is merging into the Effect-TS ecosystem and that Effect-TS can be regarded as the successor to fp-ts v2, effectively a v3. For a new build that is the decision to make. For an existing fp-ts codebase the package is still MIT licensed, still on npm at 2.16.11 in `package.json`, and still documented, so nothing forces a rewrite. Keep a single version of fp-ts installed to avoid the `tsc` hang, stay on a TypeScript new enough for your line, and read the merger announcement before planning any long-lived work.

## FAQ

### What is fp-ts?

It is a TypeScript library for typed functional programming, providing data types and type classes such as Option, Either, IO, Task, Functor, Applicative and Monad so you can write pure code composed through higher order abstractions. Its distinctive feature is an implementation of higher kinded types, which TypeScript does not support natively.

### Can you explain functional programming in TypeScript?

The short version is that you model absence and failure as values rather than as control flow, and compose operations through type classes instead of nesting calls. fp-ts ships the structures for that: Option for optional values, Either for expected failures, IO and Task for deferred effects, and Functor, Applicative and Monad to abstract over how those structures compose. The README is explicit that teaching functional programming is out of scope, so this library assumes you already know the concepts.

### What does FP stand for in software engineering?

Functional programming. In the context of this repository it names the fp-ts package, whose npm description is literally Functional programming in TypeScript, and whose stated inspirations are Haskell, PureScript and Scala. The abbreviation is also reused in a community Slack workspace, which is where the project's support channel lives.

## Sources

- [gcanti/fp-ts on GitHub](https://github.com/gcanti/fp-ts)
- [License: MIT](https://github.com/gcanti/fp-ts/blob/master/LICENSE)
- [Project website](https://gcanti.github.io/fp-ts/)
- [README](https://github.com/gcanti/fp-ts/blob/master/README.md)
- [Releases](https://github.com/gcanti/fp-ts/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/gcanti-fp-ts
