# type-fest type-fest is a comprehensive TypeScript utility types library providing over 180 essential type transformations for type-level programming. It offers pure compile-time types with zero runtime overhead, serving as a foundational toolkit for advanced TypeScript applications. The library extends TypeScript's built-in utility types with stricter versions, deep transformations, case conversions, and specialized type guards that enable developers to build robust type-safe applications. This project operates entirely at the type level with no runtime code, making it ideal for library authors and application developers who need precise type manipulation. The types are organized into categories including object manipulation, string transformations, array utilities, JSON serialization, async operations, and type guards. Each type is designed with real-world use cases in mind, prioritizing correctness and developer experience through meaningful names and comprehensive documentation. ## APIs and Key Functions ### Except - Strict Object Key Omission Creates a type by removing specified keys from an object type with stricter checking than the built-in `Omit`. Unlike `Omit`, `Except` ensures the omitted keys actually exist on the source type, preventing typos and enabling better refactoring support. ```typescript import type {Except} from 'type-fest'; interface User { id: number; email: string; password: string; createdAt: Date; } // Remove sensitive fields from public API response type PublicUser = Except; // => { id: number; email: string; createdAt: Date } const user: User = { id: 1, email: 'user@example.com', password: 'secret', createdAt: new Date() }; const publicUser: PublicUser = { id: user.id, email: user.email, createdAt: user.createdAt }; // Strict mode prevents assigning removed properties type StrictPublicUser = Except; // @ts-expect-error - Cannot assign password const invalid: StrictPublicUser = {...publicUser, password: 'hack'}; ``` ### Merge - Type-Safe Object Merging Merges two object types where properties from the second type override those in the first, properly handling index signatures and creating a simplified flattened type. ```typescript import type {Merge} from 'type-fest'; interface DatabaseConfig { host: string; port: number; timeout: number; } interface ProductionOverrides { port: 3306; ssl: boolean; poolSize: number; } type ProductionConfig = Merge; // => { host: string; port: 3306; timeout: number; ssl: boolean; poolSize: number } const defaultConfig: DatabaseConfig = { host: 'localhost', port: 5432, timeout: 5000 }; const prodConfig: ProductionConfig = { host: defaultConfig.host, port: 3306, // Overridden type timeout: defaultConfig.timeout, ssl: true, poolSize: 10 }; ``` ### LiteralUnion - Autocomplete-Preserving Union Types Creates union types combining literals with primitives while preserving IDE autocomplete for the literal values, solving TypeScript's limitation where combining `string | 'literal'` loses autocomplete. ```typescript import type {LiteralUnion} from 'type-fest'; // Without LiteralUnion - no autocomplete type Color = 'red' | 'green' | 'blue' | string; // With LiteralUnion - preserves autocomplete for known colors type BetterColor = LiteralUnion<'red' | 'green' | 'blue', string>; function setTheme(color: BetterColor) { console.log(`Theme set to ${color}`); } // IDE will autocomplete 'red', 'green', 'blue' setTheme('red'); setTheme('green'); // But also accepts any other string setTheme('#FF5733'); setTheme('custom-color-name'); ``` ### CamelCase - String Case Transformation Converts string literal types to camelCase format, useful for transforming API responses, database columns, or command-line flags to JavaScript conventions. ```typescript import type {CamelCase} from 'type-fest'; // Transform API response keys interface ApiResponse { 'user-name': string; 'email_address': string; 'is-active': boolean; 'created_at': string; } type CamelCasedApi = { [K in keyof ApiResponse as CamelCase]: ApiResponse[K] }; // => { userName: string; emailAddress: string; isActive: boolean; createdAt: string } // Transform database result to JavaScript convention const dbResult: ApiResponse = { 'user-name': 'john_doe', 'email_address': 'john@example.com', 'is-active': true, 'created_at': '2025-01-01' }; const transformed: CamelCasedApi = { userName: dbResult['user-name'], emailAddress: dbResult['email_address'], isActive: dbResult['is-active'], createdAt: dbResult['created_at'] }; ``` ### PartialDeep - Recursive Optional Properties Creates a deeply optional version of a type, making all nested properties optional. Supports configuration for array recursion and handling of undefined values. ```typescript import type {PartialDeep} from 'type-fest'; interface AppSettings { theme: { colors: { primary: string; secondary: string; }; fontSize: number; }; features: { autoSave: boolean; spellCheck: boolean; }; } const defaultSettings: AppSettings = { theme: { colors: { primary: '#007bff', secondary: '#6c757d' }, fontSize: 14 }, features: { autoSave: true, spellCheck: true } }; // Apply partial user settings deeply function applySettings( defaults: AppSettings, userSettings: PartialDeep ): AppSettings { return { theme: { colors: { primary: userSettings.theme?.colors?.primary ?? defaults.theme.colors.primary, secondary: userSettings.theme?.colors?.secondary ?? defaults.theme.colors.secondary }, fontSize: userSettings.theme?.fontSize ?? defaults.theme.fontSize }, features: { autoSave: userSettings.features?.autoSave ?? defaults.features.autoSave, spellCheck: userSettings.features?.spellCheck ?? defaults.features.spellCheck } }; } // User can override only specific nested properties const customSettings = applySettings(defaultSettings, { theme: { colors: { primary: '#ff0000' // Only override primary color } } }); ``` ### Jsonify - JSON Serialization Type Transformation Transforms TypeScript types to their JSON-serialized equivalents, handling special cases like Date objects, functions, symbols, and typed arrays that are transformed or removed during JSON serialization. ```typescript import type {Jsonify} from 'type-fest'; interface UserActivity { userId: number; action: string; timestamp: Date; metadata: Map; handler: () => void; data?: undefined; } // Transform to JSON-safe type type SerializedActivity = Jsonify; // => { userId: number; action: string; timestamp: string; metadata: {} } // Note: Date becomes string, Map becomes {}, function and undefined are removed const activity: UserActivity = { userId: 123, action: 'login', timestamp: new Date('2025-01-15'), metadata: new Map([['ip', '192.168.1.1']]), handler: () => console.log('handled'), data: undefined }; // Type-safe JSON parsing const json = JSON.stringify(activity); const parsed: SerializedActivity = JSON.parse(json); // parsed.timestamp is now typed as string, not Date // parsed.metadata is typed as {}, not Map // parsed.handler doesn't exist in type function processSerializedActivity(data: SerializedActivity) { console.log(`User ${data.userId} performed ${data.action}`); // data.timestamp is correctly typed as string const date = new Date(data.timestamp); console.log(`At ${date.toISOString()}`); } processSerializedActivity(parsed); ``` ### Simplify - Type Display Flattening Flattens intersection types and transforms interfaces into types for better IDE hints and type assignability, particularly useful when working with composed types. ```typescript 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 ComplexWidget = Position & Size & Styles; // With Simplify - IDE shows flattened type type SimpleWidget = Simplify; // => { x: number; y: number; width: number; height: number; color: string; opacity: number } const widget: SimpleWidget = { x: 10, y: 20, width: 100, height: 50, color: '#ff0000', opacity: 0.8 }; // Also useful for interface to type conversion interface ApiConfig { endpoint: string; timeout: number; } // Functions requiring Record won't accept interfaces declare function validateConfig(config: Record): boolean; const config: ApiConfig = {endpoint: '/api', timeout: 5000}; // @ts-expect-error - Interface not assignable to Record // validateConfig(config); // Works with Simplify validateConfig(config as Simplify); ``` ### RequireAtLeastOne - Conditional Required Properties Creates a type requiring at least one of the specified keys to be present, useful for configuration objects with alternative required fields. ```typescript import type {RequireAtLeastOne} from 'type-fest'; interface SearchOptions { query?: string; userId?: number; email?: string; tags?: string[]; } // Require at least one search criterion type ValidSearchOptions = RequireAtLeastOne; function searchUsers(options: ValidSearchOptions) { if (options.query) { console.log(`Searching by query: ${options.query}`); } else if (options.userId) { console.log(`Searching by userId: ${options.userId}`); } else if (options.email) { console.log(`Searching by email: ${options.email}`); } } // Valid calls searchUsers({query: 'john'}); searchUsers({userId: 123}); searchUsers({email: 'john@example.com'}); searchUsers({query: 'john', tags: ['active']}); // @ts-expect-error - Must provide at least one of: query, userId, or email searchUsers({tags: ['active']}); // @ts-expect-error - Empty object not allowed searchUsers({}); ``` ### ReadonlyDeep - Deep Immutability Creates deeply immutable versions of types, making all nested properties readonly recursively, including arrays, maps, and sets. ```typescript import type {ReadonlyDeep} from 'type-fest'; interface MutableState { users: Array<{ id: number; profile: { name: string; settings: { theme: string; notifications: boolean; }; }; }>; metadata: Map; } type ImmutableState = ReadonlyDeep; const state: ImmutableState = { users: [ { id: 1, profile: { name: 'John', settings: { theme: 'dark', notifications: true } } } ], metadata: new Map([['version', '1.0']]) }; // All of these produce TypeScript errors: // @ts-expect-error - Cannot modify readonly array // state.users.push({id: 2, profile: {name: 'Jane', settings: {theme: 'light', notifications: false}}}); // @ts-expect-error - Cannot modify nested readonly property // state.users[0].profile.name = 'Jane'; // @ts-expect-error - Cannot modify deeply nested property // state.users[0].profile.settings.theme = 'light'; // @ts-expect-error - Cannot modify readonly map // state.metadata.set('newKey', 'value'); // Reading is allowed console.log(state.users[0].profile.name); // 'John' console.log(state.users[0].profile.settings.theme); // 'dark' ``` ### ConditionalPick - Conditional Property Selection Picks properties from a type where the property values match a specified condition, useful for extracting properties of specific types. ```typescript import type {ConditionalPick} from 'type-fest'; interface ApiEndpoint { path: string; method: 'GET' | 'POST' | 'PUT' | 'DELETE'; timeout: number; retries: number; handler: (req: any) => Promise; validate: (data: any) => boolean; middleware: Array<(req: any) => void>; } // Extract only function properties type EndpointFunctions = ConditionalPick; // => { handler: (req: any) => Promise; validate: (data: any) => boolean } // Extract only string properties type EndpointStrings = ConditionalPick; // => { path: string; method: 'GET' | 'POST' | 'PUT' | 'DELETE' } // Extract only number properties type EndpointNumbers = ConditionalPick; // => { timeout: number; retries: number } const endpoint: ApiEndpoint = { path: '/api/users', method: 'GET', timeout: 5000, retries: 3, handler: async (req) => ({users: []}), validate: (data) => !!data, middleware: [] }; const functions: EndpointFunctions = { handler: endpoint.handler, validate: endpoint.validate }; const config: EndpointStrings = { path: endpoint.path, method: endpoint.method }; ``` ### AsyncReturnType - Unwrap Promise Return Types Extracts the resolved type from a Promise-returning function, useful for typing async function results without manually unwrapping the Promise. ```typescript import type {AsyncReturnType} from 'type-fest'; async function fetchUserData(userId: number) { const response = await fetch(`/api/users/${userId}`); const data = await response.json(); return { id: userId, name: data.name, email: data.email, roles: data.roles as string[], lastLogin: new Date(data.lastLogin) }; } // Extract the resolved type type UserData = AsyncReturnType; // => { id: number; name: any; email: any; roles: string[]; lastLogin: Date } async function processUser(userId: number) { const userData: UserData = await fetchUserData(userId); console.log(`Processing user: ${userData.name}`); console.log(`Email: ${userData.email}`); console.log(`Roles: ${userData.roles.join(', ')}`); console.log(`Last login: ${userData.lastLogin.toISOString()}`); return userData; } // Use in callbacks and handlers function createUserHandler(processor: (data: UserData) => void) { return async (userId: number) => { const data = await fetchUserData(userId); processor(data); }; } const handler = createUserHandler((user) => { // user is correctly typed as UserData console.log(`Handler received user: ${user.name}`); }); ``` ### Paths - Generate Property Path Union Generates a union of all possible paths to properties in a nested object, useful for type-safe property access in utilities like lodash's get/set. ```typescript import type {Paths} from 'type-fest'; interface Config { database: { host: string; port: number; credentials: { username: string; password: string; }; }; api: { endpoint: string; timeout: number; }; } type ConfigPath = Paths; // => 'database' | 'database.host' | 'database.port' | 'database.credentials' // | 'database.credentials.username' | 'database.credentials.password' // | 'api' | 'api.endpoint' | 'api.timeout' function getConfigValue( config: Config, path: Path ): any { const keys = path.split('.'); let value: any = config; for (const key of keys) { value = value[key]; } return value; } const config: Config = { database: { host: 'localhost', port: 5432, credentials: { username: 'admin', password: 'secret' } }, api: { endpoint: 'https://api.example.com', timeout: 5000 } }; // Type-safe path access with autocomplete const host = getConfigValue(config, 'database.host'); // 'localhost' const username = getConfigValue(config, 'database.credentials.username'); // 'admin' const endpoint = getConfigValue(config, 'api.endpoint'); // 'https://api.example.com' // @ts-expect-error - Invalid path const invalid = getConfigValue(config, 'database.invalid.path'); ``` ### IsEqual - Type Equality Checker Returns a boolean type indicating whether two types are exactly equal, useful for type guards and conditional type logic. ```typescript import type {IsEqual} from 'type-fest'; type Result1 = IsEqual; // => true type Result2 = IsEqual; // => false type Result3 = IsEqual<{a: number}, {a: number}>; // => true type Result4 = IsEqual<{a: number}, {a: number; b?: string}>; // => false // Use in conditional types type StrictExtract = T extends U ? IsEqual extends true ? T : never : never; // Practical example: Ensure exact match in mapped types type Status = 'pending' | 'active' | 'completed'; function validateStatus( status: T, expected: IsEqual extends true ? Status : T ): boolean { return true; } // Create type-safe validators interface ValidationRule { validate: (value: unknown) => value is T; equals: (other: U) => IsEqual; } function createValidator(): ValidationRule { return { validate: (value: unknown): value is T => { // validation logic return true; }, equals: (other: U): IsEqual => { return true as any; } }; } const stringValidator = createValidator(); const numberValidator = createValidator(); // Type-level comparison type AreEqual = ReturnType>; // true type AreNotEqual = ReturnType>; // false ``` ## Summary type-fest serves as an essential toolkit for TypeScript developers working on projects requiring sophisticated type manipulation. The library excels in scenarios like API response transformation, configuration management, state management with immutability, JSON serialization type safety, and building type-safe utility functions. Common integration patterns include transforming external API types to internal application types using case conversion utilities, enforcing deep immutability in state management systems, creating type-safe configuration loaders with partial deep merging, building runtime validators that leverage compile-time type information, and composing complex conditional types for advanced generic functions. The library's zero-runtime philosophy makes it ideal for both application and library development, as it adds no bundle size overhead while providing comprehensive compile-time safety. Developers typically import individual types as needed rather than importing the entire library, ensuring optimal tree-shaking. The types are designed to compose well together—for example, combining `Simplify` with `Merge` for readable merged types, or using `Jsonify` with `AsyncReturnType` for API response typing. The extensive test suite and active maintenance ensure reliability for production use, while the clear documentation and practical examples lower the learning curve for adopting advanced TypeScript patterns in real-world projects.