Zygos vs Neverthrow: Which Result Type for TypeScript?
Neverthrow popularized the Result pattern in TypeScript: a way to make errors explicit in function signatures instead of relying on try/catch. It's well-designed and widely adopted.
Zygos is a micro-implementation of the same pattern: 100% API compatible with Neverthrow, but 2.6x smaller. It also provides Option, Either, Task, and TaskEither monads for projects that need more than just Result. This page compares both libraries across the dimensions that matter in production.
At a Glance
| Aspect | Zygos | Neverthrow |
|---|---|---|
| Result API | 100% compatible | — |
| ResultAsync API | 100% compatible | — |
| Bundle size | ~774 B (2.6x smaller) | ~1.96 kB |
| Type safety | Zero any types | Uses any in some places |
| Additional monads | Option, Either, Task, TaskEither | Result only |
| fp-ts bridges | Yes (fromOption, fromEither, toEither) | No |
| Dependencies | Zero | Zero |
| Migration effort | Import change only | — |
Bundle Size
Zygos Result is 2.6x smaller (~774 B vs ~1.96 kB) than Neverthrow (gzipped). The difference comes from implementation approach: Zygos uses simple object literals ({ _tag: "Ok", value }) while Neverthrow uses class instantiation with more overhead.
For detailed per-module comparisons with auto-generated data, see the Zygos bundle size comparison.
// Zygos: Result + ResultAsync
import { ok, err, Result } from "pithos/zygos/result/result";
import { ResultAsync } from "pithos/zygos/result/result-async";
// Neverthrow: importing any function pulls the full library
import { ok, err, Result, ResultAsync } from "neverthrow";
Performance
Zygos is 2-4x faster on object creation and chained operations. Simple property checks (isOk, isErr, unwrapOr) perform identically in both libraries.
For detailed benchmark results with auto-generated data, see the Zygos performance benchmarks.
Key findings:
- Object creation (
ok(),err()): Zygos is 2-3x faster (object literals vs class instantiation) - Chained operations (
andThen): Zygos is 2-4x faster - Simple checks (
isOk,unwrapOr): equivalent, both are property access
100% API Compatibility
Zygos is a drop-in replacement. Every Result and ResultAsync method works identically:
// Just swap the import, zero code changes
import { ok, err, Result, safeTry } from "pithos/zygos/result/result";
import { ResultAsync, okAsync, errAsync } from "pithos/zygos/result/result-async";
// All Neverthrow patterns work as-is
const result = ok(5)
.map(x => x * 2)
.mapErr(e => `Error: ${e}`)
.andThen(x => x > 0 ? ok(x) : err("negative"));
const asyncResult = ResultAsync.fromPromise(
fetch("/api/data"),
() => "Network error"
);
const combined = Result.combine([ok(1), ok(2), ok(3)]); // Ok([1, 2, 3])
For the complete compatibility matrix, see the Zygos ↔ Neverthrow interoperability page.
What Zygos Adds Beyond Neverthrow
fp-ts Bridges
Convert between Result and fp-ts types without manual mapping:
import { fromOption, fromEither, toEither } from "pithos/zygos/result/result";
// Option → Result
const fromSome = fromOption(() => "No value")(someOption); // Ok(42)
// Either → Result
const fromRight = fromEither(rightEither); // Ok(42)
// Result → Either
const either = toEither(ok(42)); // { _tag: "Right", right: 42 }
Additional Monads
Zygos provides Option, Either, Task, and TaskEither, lightweight implementations compatible with fp-ts:
// Option: explicit absence (no null/undefined)
import { some, none, fromNullable } from "pithos/zygos/option";
// Either, Task, TaskEither: fp-ts compatible
import * as E from "pithos/zygos/either";
import * as T from "pithos/zygos/task";
import * as TE from "pithos/zygos/task-either";
Zero any Types
Neverthrow uses any in some internal types. Zygos uses unknown throughout, providing stricter type safety and catching more errors at compile time.
safeAsyncTry
Simplified async error handling without generators:
import { safeAsyncTry } from "pithos/zygos/result/result";
const result = await safeAsyncTry(() => fetch("/api/users/123").then(r => r.json()));
Migration Guide
Ready to migrate? The complete step-by-step migration guide is in the Zygos module documentation: install, swap imports, and start using additional features. Go to migration guide →
Not sure which library fits your use case? See the comparison overview.
Further Reading
- Zygos bundle size comparison: auto-generated per-module size data
- Zygos performance benchmarks: auto-generated benchmark results
- Zygos ↔ Neverthrow interoperability: full API compatibility matrix
- Zygos module documentation: API overview and usage guide
- Zygos API reference: complete function reference