mcpField
T
type-fest

type-fest

github.com/izidoromeDevelopment0151 installsUpdated 40w ago
Repository

About type-fest

type-fest is a collection of 180+ compile-time utility types for TypeScript developers, with no runtime overhead. It supports complex transformations and stricter type constraints beyond built-in utilities.

Key features

  1. 01180+ compile-time utility types
  2. 02Deep optional and readonly transformations
  3. 03Object merging and strict key selection
  4. 04JSON and case conversion types
  5. 05Path and async return type extraction

Use cases

  1. 01Transform API request and response types
  2. 02Type nested configuration updates
  3. 03Apply deep immutability to state
  4. 04Type JSON serialization results

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/izidorome/skills --skill type-fest

After installing, type/type-festin Claude Code to confirm it loaded.

Details

From the project README

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 → string
  • Map, 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

Needtype-fest UtilityAlternative
Remove keys (strict)Except<T, K>Omit<T, K> (lenient)
Merge objectsMerge<T, U>T & U (intersection)
Deep partialPartialDeep<T>Partial<T> (shallow)
Deep readonlyReadonlyDeep<T>Readonly<T> (shallow)
JSON-safe typesJsonify<T>Manual transformation
Case conversionCamelCase<T>Manual mapping
Extensible enumsLiteralUnion<T, U>T | U (no autocomplete)
Unwrap PromiseAsyncReturnType<T>Manual extraction
Require some keysRequireAtLeastOne<T, K>Custom conditional types
Pick by typeConditionalPick<T, C>Custom mapped types
Type pathsPaths<T>String literals
Flatten typesSimplify<T>Intersection types

Additional Utilities

Also available (180+ total):

  • RequireExactlyOne - Exactly one key required
  • SetRequired, SetOptional - Modify individual properties
  • SnakeCase, KebabCase, PascalCase - Other case conversions
  • IsEqual<T, U> - Type equality checking
  • Get<T, Path> - Type-safe property access
  • Opaque<T> - Branded types
  • PromiseValue<T> - Extract Promise value
  • Plus 170+ more specialized utilities

Browse the full list: https://github.com/sindresorhus/type-fest

Resources

Comments

Markdown supported