TypeScript utility types library with 180+ compile-time transformations for strict type operations, deep object manipulation, case conversions, and JSON-safe typing. Use when building type-safe APIs, config systems, state management, transforming external data schemas or when you are creating a new type.
type-fest provides 180+ compile-time utility types with zero runtime overhead for production TypeScript applications. Use it when built-in TypeScript utilities are insufficient for strict type operations, deep transformations, or complex type-level programming.
Activate for:
Skip for:
Partial, Pick, Omitpnpm add type-fest
Import granularly for optimal tree-shaking:
import type { Except, Merge, PartialDeep, Jsonify } from "type-fest";
Except<T, K> - Strict Property RemovalUse when: Removing keys from objects with compile-time verification.
Advantage over Omit: Catches typos - requires removed keys to actually exist.
import type { Except } from "type-fest";
interface User {
id: number;
email: string;
password: string;
role: string;
}
// Remove sensitive fields for public API
type PublicUser = Except<User, "password">;
// Result: { id: number; email: string; role: string }
// Catches typos
type Bad = Except<User, "pasword">; // ✗ Error: 'pasword' doesn't exist
// Strict mode prevents re-adding omitted props
type StrictPublic = Except<User, "password", { requireExactProps: true }>;
Merge<T, U> - Type-Safe Object MergingUse when: Combining types where second type's properties override the first.
import type { Merge } from "type-fest";
interface BaseConfig {
host: string;
port: number;
timeout: number;
}
interface ProdOverrides {
port: 3306; // Literal type
ssl: boolean;
poolSize: number;
}
type ProdConfig = Merge<BaseConfig, ProdOverrides>;
// Result: { host: string; port: 3306; timeout: number; ssl: boolean; poolSize: number }
PartialDeep<T> - Recursive Optional PropertiesUse when: Deep configuration objects or nested partial updates.
import type { PartialDeep } from "type-fest";
interface AppSettings {
theme: {
colors: {
primary: string;
secondary: string;
};
fontSize: number;
};
features: {
autoSave: boolean;
};
}
// User can override any nested property
function updateSettings(updates: PartialDeep<AppSettings>) {
// updates.theme?.colors?.primary is allowed
}
// Valid calls
updateSettings({ theme: { colors: { primary: "#ff0000" } } });
updateSettings({ features: { autoSave: false } });
updateSettings({ theme: { fontSize: 16 } });
Jsonify<T> - JSON Serialization TypesUse when: Typing API responses/requests that undergo JSON serialization.
What it does:
Date → stringMap, Set → {}undefined → removedimport type { Jsonify } from "type-fest";
interface Activity {
userId: number;
timestamp: Date;
metadata: Map<string, string>;
handler: () => void;
}
type ApiActivity = Jsonify<Activity>;
// Result: { userId: number; timestamp: string; metadata: {} }
// (handler and Map removed, Date → string)
async function fetchActivity(id: string): Promise<Activity> {
const response = await fetch(`/api/activity/${id}`);
const json: ApiActivity = await response.json();
// Reconstruct proper types
return {
userId: json.userId,
timestamp: new Date(json.timestamp), // string → Date
metadata: new Map(),
handler: () => console.log("loaded"),
};
}
ReadonlyDeep<T> - Deep ImmutabilityUse when: Enforcing immutability in state management, config objects.
import type {ReadonlyDeep} from 'type-fest';
interface State {
users: Array<{
id: number;
profile: {name: string};
}>;
}
type ImmutableState = ReadonlyDeep<State>;
const state: ImmutableState = {users: [{id: 1, profile: {name: 'John'}}]};
// All modification attempts cause errors
state.users.push(...); // ✗ Error
state.users[0].profile.name = 'Jane'; // ✗ Error
CamelCase<T> - Case TransformationUse when: Converting API/database naming to JavaScript conventions.
import type { CamelCase } from "type-fest";
interface DbUser {
"user-id": number;
email_address: string;
created_at: string;
}
type AppUser = {
[K in keyof DbUser as CamelCase<K>]: DbUser[K];
};
// Result: { userId: number; emailAddress: string; createdAt: string }
// Also available: SnakeCase, KebabCase, PascalCase
LiteralUnion<T, U> - Autocomplete-Preserving UnionsUse when: Creating extensible string enums that preserve IDE autocomplete.
Problem solved: 'red' | 'blue' | string loses autocomplete for 'red' and 'blue'.
import type { LiteralUnion } from "type-fest";
// Without LiteralUnion - no autocomplete
type BadColor = "red" | "blue" | "green" | string;
// With LiteralUnion - IDE autocompletes known values
type Color = LiteralUnion<"red" | "blue" | "green", string>;
function setTheme(color: Color) {
// IDE suggests 'red', 'blue', 'green'
// But also accepts '#FF5733', 'custom-color', etc.
}
AsyncReturnType<T> - Unwrap Promise TypesUse when: Typing async function results without manually unwrapping Promise.
import type { AsyncReturnType } from "type-fest";
async function fetchUser(id: number) {
const res = await fetch(`/api/users/${id}`);
const data = await res.json();
return {
id,
name: data.name,
email: data.email,
roles: data.roles as string[],
};
}
type User = AsyncReturnType<typeof fetchUser>;
// Result: { id: number; name: any; email: any; roles: string[] }
// Use in callbacks
function processUsers(users: User[]) {
users.forEach((u) => console.log(u.name));
}
RequireAtLeastOne<T, K> - Conditional Required PropsUse when: Enforcing "at least one of these fields must be present".
import type { RequireAtLeastOne } from "type-fest";
interface SearchParams {
query?: string;
userId?: number;
email?: string;
tags?: string[];
}
type ValidSearch = RequireAtLeastOne<
SearchParams,
"query" | "userId" | "email"
>;
function search(params: ValidSearch) {
// At least one of: query, userId, or email is guaranteed
}
search({ query: "test" }); // ✓
search({ userId: 123, tags: ["tag"] }); // ✓
search({ tags: ["tag"] }); // ✗ Error: need query, userId, or email
ConditionalPick<T, Condition> - Select by TypeUse when: Extracting properties that match a specific type.
import type { ConditionalPick } from "type-fest";
interface Endpoint {
path: string;
method: "GET" | "POST";
timeout: number;
retries: number;
handler: (req: any) => Promise<any>;
validate: (data: any) => boolean;
}
// Extract only functions
type EndpointFns = ConditionalPick<Endpoint, Function>;
// Result: { handler: ...; validate: ... }
// Extract only numbers
type EndpointNums = ConditionalPick<Endpoint, number>;
// Result: { timeout: number; retries: number }
Paths<T> - Type-Safe Property PathsUse when: Building type-safe get/set utilities like lodash.
import type { Paths } from "type-fest";
interface Config {
database: {
host: string;
credentials: {
user: string;
password: string;
};
};
api: { endpoint: string };
}
type ConfigPath = Paths<Config>;
// Union: 'database' | 'database.host' | 'database.credentials' |
// 'database.credentials.user' | 'database.credentials.password' |
// 'api' | 'api.endpoint'
function get<P extends ConfigPath>(config: Config, path: P): any {
return path.split(".").reduce((obj: any, key) => obj?.[key], config);
}
get(config, "database.credentials.user"); // ✓ Autocompletes
get(config, "invalid.path"); // ✗ Type error
Simplify<T> - Flatten Intersection TypesUse when: Improving IDE hints and type assignability for composed types.
import type { Simplify } from "type-fest";
type Position = { x: number; y: number };
type Size = { width: number; height: number };
type Styles = { color: string; opacity: number };
// Without Simplify - IDE shows: Position & Size & Styles
type Complex = Position & Size & Styles;
// With Simplify - IDE shows flat object
type Simple = Simplify<Position & Size & Styles>;
// Result: { x: number; y: number; width: number; height: number; color: string; opacity: number }
import type {
Except,
Jsonify,
CamelCase,
AsyncReturnType,
ReadonlyDeep,
} from "type-fest";
// External API (snake_case)
interface ApiUser {
user_id: number;
email_address: string;
created_at: string;
}
// Internal app (camelCase)
type AppUser = {
[K in keyof ApiUser as CamelCase<K>]: ApiUser[K];
};
// Public user (no sensitive data)
type PublicUser = Except<AppUser, "emailAddress">;
// Immutable state
type UserState = ReadonlyDeep<AppUser>;
class UserService {
async getUser(id: number): Promise<AppUser> {
const res = await fetch(`/api/users/${id}`);
const data: Jsonify<ApiUser> = await res.json();
return {
userId: data.user_id,
emailAddress: data.email_address,
createdAt: data.created_at,
};
}
}
type User = AsyncReturnType<UserService["getUser"]>;
import type { PartialDeep, Merge, Simplify } from "type-fest";
interface BaseConfig {
database: { host: string; port: number };
api: { timeout: number };
}
interface EnvOverrides {
database: { port: 3306 };
api: { retries: number };
}
// Merge and flatten
type FinalConfig = Simplify<Merge<BaseConfig, EnvOverrides>>;
function loadConfig(
defaults: BaseConfig,
overrides: PartialDeep<EnvOverrides>,
): FinalConfig {
// Deep merge logic
return { ...defaults, ...overrides } as FinalConfig;
}
import type { RequireAtLeastOne, Merge } from "type-fest";
interface BaseRequest {
timeout?: number;
headers?: Record<string, string>;
}
interface RequiredFields {
url: string;
}
type HttpRequest = RequireAtLeastOne<Merge<RequiredFields, BaseRequest>, "url">;
class HttpClient {
request(config: HttpRequest) {
// config.url guaranteed to exist
fetch(config.url, {
method: "GET",
headers: config.headers,
});
}
}
Combine utilities for powerful type transformations:
import type { Merge, Simplify, PartialDeep, Except } from "type-fest";
// Base + overrides + flatten
type CleanMerge<T, U> = Simplify<Merge<T, U>>;
// Remove sensitive + deep partial
type SafePartial<T> = PartialDeep<Except<T, "password" | "secret">>;
// Chain multiple operations
type ComplexType = Simplify<Merge<Merge<BaseType, OverrideType>, FinalType>>;
CRITICAL: type-fest types don't validate at runtime.
// DON'T assume runtime safety
function process(data: Jsonify<MyType>) {
const date = new Date(data.timestamp); // Could fail if data is malformed
}
// DO add runtime validation
function process(data: Jsonify<MyType>) {
if (typeof data.timestamp !== "string") {
throw new Error("Invalid timestamp");
}
const date = new Date(data.timestamp);
}
Avoid excessive PartialDeep/ReadonlyDeep nesting (3-4 levels max):
// DON'T
type Bad = PartialDeep<PartialDeep<DeepType>>;
// DO
type Good = PartialDeep<DeepType>;
Tree-shaking works best with granular imports:
// DO
import type { Except, Merge } from "type-fest";
// DON'T
import type * as TypeFest from "type-fest";
LiteralUnion ScopeDon't use with already-narrow types:
// DON'T
type Bad = LiteralUnion<"active", "active" | "inactive">;
// DO
type Good = LiteralUnion<"active" | "inactive", string>;
| Need | type-fest Utility | Alternative |
|---|---|---|
| Remove keys (strict) | Except<T, K> | Omit<T, K> (lenient) |
| Merge objects | Merge<T, U> | T & U (intersection) |
| Deep partial | PartialDeep<T> | Partial<T> (shallow) |
| Deep readonly | ReadonlyDeep<T> | Readonly<T> (shallow) |
| JSON-safe types | Jsonify<T> | Manual transformation |
| Case conversion | CamelCase<T> | Manual mapping |
| Extensible enums | LiteralUnion<T, U> | T | U (no autocomplete) |
| Unwrap Promise | AsyncReturnType<T> | Manual extraction |
| Require some keys | RequireAtLeastOne<T, K> | Custom conditional types |
| Pick by type | ConditionalPick<T, C> | Custom mapped types |
| Type paths | Paths<T> | String literals |
| Flatten types | Simplify<T> | Intersection types |
Also available (180+ total):
RequireExactlyOne - Exactly one key requiredSetRequired, SetOptional - Modify individual propertiesSnakeCase, KebabCase, PascalCase - Other case conversionsIsEqual<T, U> - Type equality checkingGet<T, Path> - Type-safe property accessOpaque<T> - Branded typesPromiseValue<T> - Extract Promise valueBrowse the full list: https://github.com/sindresorhus/type-fest
/source directory