type-fest
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.
How do I install this agent skill?
npx skills add https://github.com/izidorome/skills --skill type-festIs this agent skill safe to install?
- Gen Agent Trust Hubpass
This skill provides documentation and usage examples for the 'type-fest' TypeScript utility library. It includes installation instructions, code snippets for various utility types, and links to the project's official repository. No malicious patterns or security risks were detected.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
- Runlayerpass
1 file scanned · No issues
What does this agent skill do?
type-fest: Advanced TypeScript Utility Types
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.
When to Use This Skill
Activate for:
- API response/request type transformations (snake_case ↔ camelCase)
- Deep configuration merging and partial updates
- Immutable state management with readonly guarantees
- JSON serialization type safety (Date → string, etc.)
- Removing/selecting object properties with strict checking
- Type-safe path access and property selection
- Async function return type extraction
- Conditional type operations
Skip for:
- Runtime validation (use zod/yup/io-ts - type-fest is compile-time only)
- Simple cases covered by built-in
Partial,Pick,Omit - Runtime type guards (complement type-fest with runtime checks)
Installation
npm install type-fest
Import granularly for optimal tree-shaking:
import type {Except, Merge, PartialDeep, Jsonify} from 'type-fest';
Essential Utilities
Except<T, K> - Strict Property Removal
Use 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 Merging
Use 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 Properties
Use 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 Types
Use when: Typing API responses/requests that undergo JSON serialization.
What it does:
Date→stringMap,Set→{}- Functions,
undefined→ removed - Recursively transforms nested types
import 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 Immutability
Use 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 Transformation
Use 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 Unions
Use 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 Types
Use 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 Props
Use 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 Type
Use 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 Paths
Use 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 Types
Use 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 }
Common Patterns
API Client with Type Transformations
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']>;
Configuration Management
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;
}
Type-Safe Builders
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
});
}
}
Composition Strategy
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
>
>;
Important Gotchas
1. Runtime vs Compile-Time
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);
}
2. Deep Type Nesting Limits
Avoid excessive PartialDeep/ReadonlyDeep nesting (3-4 levels max):
// DON'T
type Bad = PartialDeep<PartialDeep<DeepType>>;
// DO
type Good = PartialDeep<DeepType>;
3. Import Only What You Need
Tree-shaking works best with granular imports:
// DO
import type {Except, Merge} from 'type-fest';
// DON'T
import type * as TypeFest from 'type-fest';
4. LiteralUnion Scope
Don't use with already-narrow types:
// DON'T
type Bad = LiteralUnion<'active', 'active' | 'inactive'>;
// DO
type Good = LiteralUnion<'active' | 'inactive', string>;
Quick Reference
| 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 |
Additional Utilities
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 value- Plus 170+ more specialized utilities
Browse the full list: https://github.com/sindresorhus/type-fest
Resources
- GitHub: https://github.com/sindresorhus/type-fest
- Docs: https://sindresorhus.com/type-fest
- Full type list: Browse
/sourcedirectory - Examples: Check test files for usage patterns
- LLM Guide: https://context7.com/sindresorhus/type-fest/llms.txt
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/izidorome/skills/type-fest">View type-fest on skillZs</a>