<!-- PkgPulse AI-readable guide source -->
<!-- Canonical: https://www.pkgpulse.com/guides/typescript-utility-packages -->
<!-- Raw Markdown: https://www.pkgpulse.com/guides/typescript-utility-packages/raw.md -->
<!-- Source path: content/guides/typescript-utility-packages.mdx -->

---
og_image: "/images/guides/typescript-utility-packages.webp"
title: "Essential TypeScript Utility Packages for 2026"
description: "The best TypeScript utility packages to know in 2026 — from validation with Zod to type helpers with ts-reset. Curated picks with real download data Updated."
date: "2026-03-04"
authors: ["team"]
tier: 1
tags: ["typescript", "utility", "zod", "packages", "roundup", "2026"]
---

TypeScript's type system is powerful, but some tasks need libraries to be practical. These utility packages fill the gaps — from runtime validation to type manipulation to developer ergonomics.

Every package here is actively maintained, well-typed, and used in production by thousands of projects. Download data from [PkgPulse](https://www.pkgpulse.com).

## Schema Validation

### Zod

The TypeScript-first schema validation library. Define a schema once, get both runtime validation and static types.

- **Downloads:** 14M/week
- **Size:** 13KB (gzip)
- **Use case:** Form validation, API input/output, config parsing

```typescript
import { z } from 'zod';

const UserSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  age: z.number().int().positive().optional(),
});

type User = z.infer<typeof UserSchema>; // TypeScript type auto-generated

const result = UserSchema.safeParse(input);
if (result.success) {
  console.log(result.data); // Fully typed
}
```

**Why it dominates:** One schema definition gives you both TypeScript types and runtime validation. No more writing types AND validation rules separately.

### Valibot

The lightweight alternative to Zod. Modular, tree-shakeable, 98% smaller in many cases.

- **Downloads:** 400K/week
- **Size:** 0.5-5KB (depending on features used)
- **Use case:** Same as Zod, but when bundle size is critical

```typescript
import * as v from 'valibot';

const UserSchema = v.object({
  name: v.pipe(v.string(), v.minLength(2)),
  email: v.pipe(v.string(), v.email()),
  age: v.optional(v.pipe(v.number(), v.integer(), v.minValue(1))),
});

type User = v.InferOutput<typeof UserSchema>;
```

**When to choose over Zod:** Client-side validation where bundle size matters. Valibot's tree-shaking means you only ship the validators you use.

## Type Utilities

### ts-reset

Fixes TypeScript's built-in types to be more useful. A single import that makes `.filter(Boolean)`, `.json()`, and `JSON.parse()` return better types.

- **Downloads:** 700K/week
- **Size:** 0KB (types only, no runtime)

```typescript
// Before ts-reset
const filtered = [1, undefined, 2].filter(Boolean);
// Type: (number | undefined)[] — still includes undefined!

// After ts-reset
const filtered = [1, undefined, 2].filter(Boolean);
// Type: number[] — correct!
```

```typescript
// Before ts-reset
const data = JSON.parse(input);
// Type: any

// After ts-reset
const data = JSON.parse(input);
// Type: unknown — forces you to validate
```

**Install it on every TypeScript project.** Zero runtime cost, immediate type safety improvements.

### type-fest

A curated collection of essential TypeScript utility types. If you've ever needed `PartialDeep`, `SetRequired`, `CamelCase`, or `Simplify` — it's here.

- **Downloads:** 30M/week
- **Size:** 0KB (types only)

```typescript
import type { PartialDeep, CamelCase, SetRequired } from 'type-fest';

// Make all nested properties optional
type DeepPartialUser = PartialDeep<User>;

// Convert string to camelCase type
type Key = CamelCase<'user-profile-settings'>; // 'userProfileSettings'

// Make specific optional fields required
type UserWithEmail = SetRequired<User, 'email'>;
```

### ts-pattern

Exhaustive pattern matching for TypeScript. Like a supercharged `switch` statement that the compiler verifies is complete.

- **Downloads:** 1M/week
- **Size:** 3KB (gzip)

```typescript
import { match, P } from 'ts-pattern';

type Shape =
  | { type: 'circle'; radius: number }
  | { type: 'rect'; width: number; height: number };

const area = match(shape)
  .with({ type: 'circle' }, ({ radius }) => Math.PI * radius ** 2)
  .with({ type: 'rect' }, ({ width, height }) => width * height)
  .exhaustive(); // Compile error if a case is missing
```

## Date & Time

### date-fns

Modular date utility library. Import only the functions you need — tree-shaking keeps your bundle small.

- **Downloads:** 18M/week
- **Size:** 2-10KB (per function, gzipped)

```typescript
import { format, addDays, differenceInDays } from 'date-fns';

format(new Date(), 'yyyy-MM-dd'); // '2026-03-04'
addDays(new Date(), 7); // Next week
differenceInDays(endDate, startDate); // Days between
```

### Day.js

Moment.js replacement with the same API but at 2KB. If you want a simple, familiar API:

- **Downloads:** 16M/week
- **Size:** 2.9KB (gzip)

```typescript
import dayjs from 'dayjs';

dayjs().format('YYYY-MM-DD');
dayjs().add(7, 'day');
dayjs(endDate).diff(startDate, 'day');
```

## Environment & Configuration

### dotenv

Load environment variables from `.env` files. Still the standard after all these years.

- **Downloads:** 35M/week
- **Size:** 2KB

### @t3-oss/env-nextjs

Type-safe environment variables for Next.js. Combines Zod validation with environment variable loading.

- **Downloads:** 500K/week

```typescript
import { createEnv } from '@t3-oss/env-nextjs';
import { z } from 'zod';

export const env = createEnv({
  server: {
    DATABASE_URL: z.string().url(),
    STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
  },
  client: {
    NEXT_PUBLIC_APP_URL: z.string().url(),
  },
  runtimeEnv: {
    DATABASE_URL: process.env.DATABASE_URL,
    STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
    NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
  },
});

// env.DATABASE_URL is guaranteed to be a valid URL string
```

## Unique ID Generation

### nanoid

Tiny, secure, URL-friendly unique ID generator. Smaller and faster than UUID.

- **Downloads:** 22M/week
- **Size:** 0.5KB (gzip)

```typescript
import { nanoid } from 'nanoid';

nanoid(); // "V1StGXR8_Z5jdHi6B-myT" (21 chars, URL-safe)
nanoid(10); // "IRFa-VaY2b" (custom length)
```

### cuid2

Collision-resistant IDs optimized for horizontal scaling and security. The successor to cuid.

- **Downloads:** 800K/week

```typescript
import { createId } from '@paralleldrive/cuid2';

createId(); // "tz4a98xxat96iws9zmbrgj3a"
```

## HTTP & Data Fetching

### ky

Tiny HTTP client built on fetch. Cleaner API than native fetch with retries, JSON shortcuts, and hooks.

- **Downloads:** 2M/week
- **Size:** 3KB (gzip)

```typescript
import ky from 'ky';

const data = await ky.get('https://api.example.com/data').json<User[]>();
// Automatic JSON parsing, typed response
```

### ofetch

Universal fetch library from the UnJS ecosystem. Works in Node.js, Deno, Bun, and browsers.

- **Downloads:** 8M/week
- **Size:** 3KB (gzip)

```typescript
import { ofetch } from 'ofetch';

const data = await ofetch<User[]>('/api/users', {
  retry: 3,
  retryDelay: 1000,
});
```

## String & Data Manipulation

### slugify

Convert strings to URL-friendly slugs.

- **Downloads:** 3M/week
- **Size:** 1KB

```typescript
import slugify from 'slugify';
slugify('Hello World!', { lower: true }); // 'hello-world'
```

### superjson

Serialize and deserialize JavaScript values that JSON.stringify can't handle — dates, maps, sets, BigInts, and more.

- **Downloads:** 3M/week
- **Size:** 4KB

```typescript
import superjson from 'superjson';

const data = { date: new Date(), set: new Set([1, 2, 3]) };
const json = superjson.stringify(data);
const parsed = superjson.parse(json); // Dates and Sets restored
```

## Our Curated Stack

If you're starting a new TypeScript project, here's our recommended utility stack:

| Need | Package | Size |
|------|---------|------|
| Validation | Zod (or Valibot for small bundles) | 13KB / 0.5KB |
| Type helpers | type-fest + ts-reset | 0KB (types only) |
| Pattern matching | ts-pattern | 3KB |
| Dates | date-fns | 2-10KB |
| HTTP client | ky or ofetch | 3KB |
| IDs | nanoid | 0.5KB |
| Env validation | @t3-oss/env-nextjs | — |

Total runtime impact: ~22KB gzipped. That's a complete utility layer for less than a single React component library.

## Common Mistakes with TypeScript Utility Packages

Even experienced TypeScript developers make predictable errors when reaching for these utility packages. Here are the most common ones and how to avoid them.

### Zod: Validating at the Wrong Layer

The most common Zod mistake is putting validation only at the API boundary and trusting the result everywhere else. Zod's real power is validating *all* external data — not just form inputs, but environment variables, `localStorage` values, third-party API responses, and URL parameters.

```typescript
// ❌ Common: only validate API inputs
app.post('/users', async (req, res) => {
  const user = UserSchema.parse(req.body); // Good
});

// But then trust data from other sources without validation:
const config = JSON.parse(fs.readFileSync('config.json', 'utf-8')); // ❌ unvalidated

// ✅ Better: validate ALL external data
const ConfigSchema = z.object({
  port: z.number().int().min(1024).max(65535),
  dbUrl: z.string().url(),
});
const config = ConfigSchema.parse(JSON.parse(fs.readFileSync('config.json', 'utf-8')));
```

### ts-reset: Installing It Wrong

`ts-reset` is a `.d.ts` file that modifies global TypeScript types. It must be included in your project's type resolution, not just installed. If you install it but don't reference it, nothing changes.

```typescript
// ❌ ts-reset installed but not referenced
// Nothing changes — TypeScript doesn't know about it

// ✅ Add to your tsconfig.json
{
  "compilerOptions": {
    "types": ["@total-typescript/ts-reset"]
  }
}

// OR: import it once in your project's global types file
// types/global.d.ts
import "@total-typescript/ts-reset";
```

### type-fest: Using It Instead of Built-ins

TypeScript's built-in utility types (`Partial`, `Required`, `Pick`, `Omit`, `ReturnType`, `Parameters`) cover the common cases. Reaching for type-fest for everything leads to unnecessary dependency weight when the standard library suffices.

```typescript
// ❌ Using type-fest when built-ins work
import type { Except } from 'type-fest';
type UserWithoutPassword = Except<User, 'password'>;

// ✅ Use built-in Omit
type UserWithoutPassword = Omit<User, 'password'>;

// ✅ Reach for type-fest when built-ins fall short
import type { PartialDeep } from 'type-fest';
type PatchPayload = PartialDeep<User>; // Deep partial — no built-in equivalent
```

### nanoid: Using It in Server-Side Rendering Without Seeding

Nanoid uses `crypto.getRandomValues` in browsers and `crypto.randomBytes` in Node.js. In SSR contexts, if you generate an ID on the server and render it in HTML, then the client tries to hydrate with a freshly generated (different) ID, you'll get a hydration mismatch. The fix: generate IDs during data fetching (before render) or persist them to state.

---

## Advanced Patterns

### Composing Zod Schemas for APIs

Real APIs aren't single schemas — they're composed from reusable pieces. Zod's composition primitives let you build a schema library that mirrors your domain model.

```typescript
// schemas/base.ts — reusable primitives
const Timestamp = z.object({
  createdAt: z.string().datetime(),
  updatedAt: z.string().datetime(),
});

const PaginatedResponse = <T extends z.ZodTypeAny>(itemSchema: T) =>
  z.object({
    items: z.array(itemSchema),
    total: z.number().int().nonnegative(),
    page: z.number().int().positive(),
    pageSize: z.number().int().positive(),
  });

// schemas/user.ts
const UserBase = z.object({
  id: z.string().cuid2(),
  name: z.string().min(2).max(100),
  email: z.string().email(),
});

export const User = UserBase.merge(Timestamp);
export const UserList = PaginatedResponse(UserBase);
export const CreateUserInput = UserBase.omit({ id: true }).merge(
  z.object({ password: z.string().min(8) })
);

// Type inference flows through
type User = z.infer<typeof User>;
type UserList = z.infer<typeof UserList>;
```

### Using ts-pattern with API Response Discriminated Unions

```typescript
import { match, P } from 'ts-pattern';

type ApiResponse<T> =
  | { status: 'success'; data: T }
  | { status: 'error'; code: number; message: string }
  | { status: 'loading' }
  | { status: 'idle' };

function renderUserProfile(response: ApiResponse<User>) {
  return match(response)
    .with({ status: 'idle' }, () => <EmptyState />)
    .with({ status: 'loading' }, () => <Skeleton />)
    .with({ status: 'error', code: 401 }, () => <LoginPrompt />)
    .with({ status: 'error', code: P.number }, ({ message }) => <ErrorBanner message={message} />)
    .with({ status: 'success' }, ({ data }) => <UserCard user={data} />)
    .exhaustive(); // TypeScript ensures all cases are handled
}
```

This pattern — combining discriminated union types with `ts-pattern`'s `.exhaustive()` — is one of the most powerful TypeScript patterns for handling state machines and API responses. The compiler tells you when you've missed a case.

### Type-Safe superjson for tRPC

When using tRPC, `superjson` as the transformer lets you pass dates, maps, and sets through your API without manual serialization:

```typescript
// server/trpc.ts
import { initTRPC } from '@trpc/server';
import superjson from 'superjson';

const t = initTRPC.create({
  transformer: superjson, // Dates flow through automatically
});

// Now returning a Date from a procedure works end-to-end
const userRouter = t.router({
  getUser: t.procedure
    .input(z.object({ id: z.string() }))
    .query(async ({ input }) => {
      const user = await db.user.findUnique({ where: { id: input.id } });
      return user; // createdAt (Date) is preserved through superjson
    }),
});
```

---

## When NOT to Use These Packages

Not every project needs every utility in the list. Here's honest guidance on when to skip them.

**Skip Zod** when you control both the data producer and consumer (e.g., internal server-to-server calls with TypeScript on both ends). Zod adds runtime overhead and complexity for validation that TypeScript's type system already guarantees at compile time.

**Skip ts-reset** if you prefer explicit `unknown` handling and have already audited your codebase for `JSON.parse` misuse. Some teams find ts-reset's implicit type narrowing surprising when onboarding new developers. Its changes are subtle but global — read the full changelog before adopting.

**Skip type-fest** if you only need one or two types from it. TypeScript 4.x+ added many utility types that type-fest pioneered. Check if `NoInfer`, `Awaited`, or `Satisfies` (now built-in) solve your problem before adding a dependency.

**Skip Day.js** if you're building a new greenfield project. The `Temporal` API is advancing through browser standardization and will eventually land as a native alternative. `date-fns` is the better bridge choice — modular, tree-shakeable, and its API aligns more closely with the Temporal model.

**Skip ofetch** in browser-only code where native `fetch` is available. Adding an abstraction over `fetch` for simple GET requests adds bytes without meaningful value. Use `ky` or `ofetch` when you need retry logic, automatic JSON parsing, or Node.js compatibility.

---

## Ecosystem Integration: These Packages in a Full Stack

Here's how the packages in this list combine in a real Next.js 15 + tRPC application:

```
┌──────────────────────────────────────────────────────┐
│                   Frontend (Client)                  │
│                                                      │
│  ofetch / ky → tRPC client → Zod-validated response │
│  nanoid for optimistic update IDs                    │
│  ts-pattern for UI state machine rendering           │
│  superjson as tRPC transformer (dates preserved)     │
└──────────────────────────────────────────────────────┘
                        │
                        ▼
┌──────────────────────────────────────────────────────┐
│              tRPC Router (Server)                    │
│                                                      │
│  @t3-oss/env-nextjs validates env at startup         │
│  Zod validates all procedure inputs                  │
│  type-fest utility types on shared interfaces        │
│  ts-reset applied globally (JSON.parse → unknown)    │
│  date-fns for date formatting in responses           │
└──────────────────────────────────────────────────────┘
                        │
                        ▼
┌──────────────────────────────────────────────────────┐
│                    Database Layer                    │
│                                                      │
│  superjson serializes complex types to/from JSON     │
│  nanoid / cuid2 for ID generation                    │
└──────────────────────────────────────────────────────┘
```

This stack uses every package in this article with purpose. Each solves a specific gap: Zod handles the untrusted boundary, ts-reset hardens the language itself, type-fest handles type manipulation, and superjson handles the serialization boundary.

The packages in this list are selected because they compose well together — each fills a distinct gap without overlapping. Zod handles untrusted boundaries, ts-reset corrects language-level type deficiencies, type-fest provides the deep utility types that TypeScript's built-ins don't cover, ts-pattern adds exhaustive discriminated union handling, and superjson bridges the serialization gap between JavaScript's rich runtime types and JSON's limited wire format. Using all of them together does not create redundancy; it creates a layered type-safety stack where each package's responsibilities are clear. The total runtime footprint — approximately 22KB gzipped — is smaller than a single icon library, making this a practical addition even to bundle-sensitive applications.

Browse all these packages on [PkgPulse](https://www.pkgpulse.com) to compare health scores, download trends, and alternatives.

*See also: [Yup vs Zod](/compare/yup-vs-zod) and [Superstruct vs Zod](/compare/superstruct-vs-zod), [AJV vs Zod vs Valibot: Speed, Bundle Size & TypeScript (2026)](/guides/ajv-vs-zod-vs-valibot-schema-validation-2026).*
