Utility Types Collection for TypeScript providing comprehensive type-level utilities for static type manipulation.
—
Advanced mapped types provide sophisticated type transformations for object manipulation, key selection, property filtering, and complex type operations. These utilities enable powerful compile-time type transformations for building type-safe applications.
import {
// Set operations
SetIntersection, SetDifference, SetComplement, SymmetricDifference,
// Object manipulation
Omit, Optional, Required, Diff, Subtract, Overwrite, Assign,
Intersection, PickByValue, PickByValueExact, OmitByValue, OmitByValueExact,
// Key selection
FunctionKeys, NonFunctionKeys, MutableKeys, WritableKeys, ReadonlyKeys,
RequiredKeys, OptionalKeys,
// Advanced utilities
NonUndefined, Unionize, PromiseType, Brand, Exact, ValuesType, UnionToIntersection,
Mutable, Writable,
// Deep transformations
DeepReadonly, DeepRequired, DeepNonNullable, DeepPartial
} from 'utility-types';For CommonJS:
const {
// Set operations
SetIntersection, SetDifference, SetComplement, SymmetricDifference,
// Object manipulation
Omit, Optional, Required, Diff, Subtract, Overwrite, Assign,
Intersection, PickByValue, PickByValueExact, OmitByValue, OmitByValueExact,
// Key selection
FunctionKeys, NonFunctionKeys, MutableKeys, WritableKeys, ReadonlyKeys,
RequiredKeys, OptionalKeys,
// Advanced utilities
NonUndefined, Unionize, PromiseType, Brand, Exact, ValuesType, UnionToIntersection,
Mutable, Writable,
// Deep transformations
DeepReadonly, DeepRequired, DeepNonNullable, DeepPartial
} = require('utility-types');Get the intersection of two union types (same as Extract).
type SetIntersection<A, B> = A extends B ? A : never;Usage:
type Colors = 'red' | 'green' | 'blue';
type Warm = 'red' | 'orange' | 'yellow';
type WarmColors = SetIntersection<Colors, Warm>;
// Result: 'red'
type StringOrNumber = string | number;
type NumberOrBoolean = number | boolean;
type CommonType = SetIntersection<StringOrNumber, NumberOrBoolean>;
// Result: number
// With function types
type AsyncFunction = () => Promise<any>;
type SyncFunction = () => any;
type AsyncFromMixed = SetIntersection<AsyncFunction | SyncFunction, Function>;
// Result: () => Promise<any> | () => anyGet the difference between two union types (same as Exclude).
type SetDifference<A, B> = A extends B ? never : A;Usage:
type AllTypes = string | number | boolean | null;
type TruthyTypes = string | number | boolean;
type FalsyTypes = SetDifference<AllTypes, TruthyTypes>;
// Result: null
type Events = 'click' | 'focus' | 'blur' | 'keydown';
type MouseEvents = 'click' | 'mousedown' | 'mouseup';
type NonMouseEvents = SetDifference<Events, MouseEvents>;
// Result: 'focus' | 'blur' | 'keydown'Get the complement of a subset within a superset.
type SetComplement<A, A1 extends A> = SetDifference<A, A1>;Usage:
type AllPermissions = 'read' | 'write' | 'delete' | 'admin';
type UserPermissions = 'read' | 'write';
type MissingPermissions = SetComplement<AllPermissions, UserPermissions>;
// Result: 'delete' | 'admin'
// Useful for ensuring exhaustive handling
function handleRemainingCases(permission: SetComplement<AllPermissions, 'read' | 'write'>) {
// permission is typed as 'delete' | 'admin'
switch (permission) {
case 'delete':
return handleDelete();
case 'admin':
return handleAdmin();
}
}Get the symmetric difference of two union types.
type SymmetricDifference<A, B> = SetDifference<A | B, A & B>;Usage:
type SetA = 'a' | 'b' | 'c';
type SetB = 'b' | 'c' | 'd';
type OnlyInAOrB = SymmetricDifference<SetA, SetB>;
// Result: 'a' | 'd'
type Frontend = 'react' | 'vue' | 'angular';
type Backend = 'node' | 'django' | 'rails' | 'vue';
type ExclusiveTechs = SymmetricDifference<Frontend, Backend>;
// Result: 'react' | 'angular' | 'node' | 'django' | 'rails'Remove specified properties from an object type.
type Omit<T, K extends keyof any> = Pick<T, SetDifference<keyof T, K>>;Usage:
interface User {
id: number;
name: string;
email: string;
password: string;
createdAt: Date;
}
type PublicUser = Omit<User, 'password'>;
// Result: { id: number; name: string; email: string; createdAt: Date; }
type UserSummary = Omit<User, 'password' | 'createdAt'>;
// Result: { id: number; name: string; email: string; }
// Works with union types
type ApiResponse =
| { type: 'success'; data: any; timestamp: number }
| { type: 'error'; message: string; timestamp: number };
type ResponseWithoutTimestamp = Omit<ApiResponse, 'timestamp'>;
// Result: { type: 'success'; data: any } | { type: 'error'; message: string }Make specified properties optional.
type Optional<T extends object, K extends keyof T = keyof T> = Omit<T, K> & Partial<Pick<T, K>>;Usage:
interface CreateUserRequest {
name: string;
email: string;
age: number;
avatar: string;
}
type CreateUserWithDefaults = Optional<CreateUserRequest, 'age' | 'avatar'>;
// Result: { name: string; email: string; age?: number; avatar?: string; }
// Make all properties optional
type PartialUser = Optional<CreateUserRequest>;
// Result: { name?: string; email?: string; age?: number; avatar?: string; }
function createUser(data: CreateUserWithDefaults) {
const user = {
...data,
age: data.age ?? 18,
avatar: data.avatar ?? '/default-avatar.png'
};
return user;
}Make specified properties required.
type Required<T extends object, K extends keyof T = keyof T> = Omit<T, K> & Required<Pick<T, K>>;Usage:
interface PartialConfig {
host?: string;
port?: number;
timeout?: number;
retries?: number;
}
type RequiredConfig = Required<PartialConfig, 'host' | 'port'>;
// Result: { host: string; port: number; timeout?: number; retries?: number; }
// Make all properties required
type FullConfig = Required<PartialConfig>;
// Result: { host: string; port: number; timeout: number; retries: number; }
function connect(config: RequiredConfig) {
// host and port are guaranteed to exist
console.log(`Connecting to ${config.host}:${config.port}`);
}Remove properties that exist in the second type from the first type.
type Diff<T extends object, U extends object> = Pick<T, SetDifference<keyof T, keyof U>>;Usage:
interface ExtendedUser {
id: number;
name: string;
email: string;
password: string;
isAdmin: boolean;
lastLogin: Date;
}
interface BasicUser {
id: number;
name: string;
email: string;
}
type UserExtensions = Diff<ExtendedUser, BasicUser>;
// Result: { password: string; isAdmin: boolean; lastLogin: Date; }
// Common pattern: finding unique properties
interface ComponentProps {
className?: string;
style?: React.CSSProperties;
children?: React.ReactNode;
onClick: () => void;
disabled: boolean;
}
interface HTMLButtonProps {
className?: string;
style?: React.CSSProperties;
children?: React.ReactNode;
disabled?: boolean;
}
type CustomProps = Diff<ComponentProps, HTMLButtonProps>;
// Result: { onClick: () => void; disabled: boolean; }Pick properties from an object type based on their value types.
type PickByValue<T, ValueType> = Pick<T, {
[Key in keyof T]: T[Key] extends ValueType ? Key : never;
}[keyof T]>;Usage:
interface MixedData {
id: number;
name: string;
count: number;
active: boolean;
tags: string[];
metadata: Record<string, any>;
}
type StringFields = PickByValue<MixedData, string>;
// Result: { name: string; }
type NumberFields = PickByValue<MixedData, number>;
// Result: { id: number; count: number; }
type ArrayFields = PickByValue<MixedData, any[]>;
// Result: { tags: string[]; }
// Useful for creating type-safe field selectors
function getStringFields<T>(obj: T): PickByValue<T, string> {
const result = {} as any;
for (const [key, value] of Object.entries(obj)) {
if (typeof value === 'string') {
result[key] = value;
}
}
return result;
}Remove properties from an object type based on their value types.
type OmitByValue<T, ValueType> = Pick<T, {
[Key in keyof T]: T[Key] extends ValueType ? never : Key;
}[keyof T]>;Usage:
interface UserData {
id: number;
name: string;
email: string;
isActive: boolean;
loginCount: number;
settings: Record<string, any>;
}
type NonStringFields = OmitByValue<UserData, string>;
// Result: { id: number; isActive: boolean; loginCount: number; settings: Record<string, any>; }
type NonNumberFields = OmitByValue<UserData, number>;
// Result: { name: string; email: string; isActive: boolean; settings: Record<string, any>; }
// Remove function properties
interface EventHandler {
name: string;
type: string;
handler: () => void;
cleanup: () => void;
priority: number;
}
type EventData = OmitByValue<EventHandler, Function>;
// Result: { name: string; type: string; priority: number; }Get keys of properties that are functions.
type FunctionKeys<T extends object> = {
[K in keyof T]-?: NonUndefined<T[K]> extends Function ? K : never;
}[keyof T];Usage:
class UserService {
name: string = 'UserService';
version: number = 1;
getUser(id: number): User { return {} as User; }
createUser(data: CreateUserData): User { return {} as User; }
deleteUser(id: number): void {}
}
type UserServiceMethods = FunctionKeys<UserService>;
// Result: 'getUser' | 'createUser' | 'deleteUser'
// Create method-only interface
type UserServiceInterface = Pick<UserService, FunctionKeys<UserService>>;
// Result: {
// getUser(id: number): User;
// createUser(data: CreateUserData): User;
// deleteUser(id: number): void;
// }Get keys of properties that are not functions.
type NonFunctionKeys<T extends object> = {
[K in keyof T]-?: NonUndefined<T[K]> extends Function ? never : K;
}[keyof T];Usage:
class DatabaseConnection {
host: string = 'localhost';
port: number = 5432;
isConnected: boolean = false;
connect(): Promise<void> { return Promise.resolve(); }
disconnect(): void {}
query(sql: string): Promise<any[]> { return Promise.resolve([]); }
}
type ConnectionData = NonFunctionKeys<DatabaseConnection>;
// Result: 'host' | 'port' | 'isConnected'
type ConnectionState = Pick<DatabaseConnection, NonFunctionKeys<DatabaseConnection>>;
// Result: { host: string; port: number; isConnected: boolean; }Get keys of properties that are required (not optional).
type RequiredKeys<T> = {
[K in keyof T]: {} extends Pick<T, K> ? never : K;
}[keyof T];Usage:
interface UserProfile {
id: number;
name: string;
email?: string;
avatar?: string;
bio?: string;
}
type RequiredUserFields = RequiredKeys<UserProfile>;
// Result: 'id' | 'name'
type MinimalUser = Pick<UserProfile, RequiredKeys<UserProfile>>;
// Result: { id: number; name: string; }
// Useful for validation
function validateRequiredFields<T>(obj: Partial<T>): obj is Pick<T, RequiredKeys<T>> {
// Implementation would check if all required fields are present
return true; // Simplified
}Get keys of properties that are optional.
type OptionalKeys<T> = {
[K in keyof T]: {} extends Pick<T, K> ? K : never;
}[keyof T];Usage:
interface CreatePostRequest {
title: string;
content: string;
tags?: string[];
publishedAt?: Date;
featuredImage?: string;
}
type OptionalPostFields = OptionalKeys<CreatePostRequest>;
// Result: 'tags' | 'publishedAt' | 'featuredImage'
type PostDefaults = Pick<CreatePostRequest, OptionalKeys<CreatePostRequest>>;
// Result: { tags?: string[]; publishedAt?: Date; featuredImage?: string; }
function applyDefaults(request: CreatePostRequest): Required<CreatePostRequest> {
return {
...request,
tags: request.tags ?? [],
publishedAt: request.publishedAt ?? new Date(),
featuredImage: request.featuredImage ?? '/default-image.jpg'
};
}Convert a union type to an intersection type.
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;Usage:
type A = { a: string };
type B = { b: number };
type C = { c: boolean };
type Union = A | B | C;
type Intersection = UnionToIntersection<Union>;
// Result: { a: string } & { b: number } & { c: boolean }
// Which is equivalent to: { a: string; b: number; c: boolean }
// Useful for combining multiple object types
interface BaseConfig { timeout: number; }
interface AuthConfig { apiKey: string; }
interface CacheConfig { cacheSize: number; }
type ConfigUnion = BaseConfig | AuthConfig | CacheConfig;
type FullConfig = UnionToIntersection<ConfigUnion>;
// Result: { timeout: number; apiKey: string; cacheSize: number; }Create a branded (nominal) type to distinguish values of the same underlying type.
type Brand<T, U> = T & { __brand: U };Usage:
type UserId = Brand<number, 'UserId'>;
type ProductId = Brand<number, 'ProductId'>;
function getUser(id: UserId): User {
// Implementation
return {} as User;
}
function getProduct(id: ProductId): Product {
// Implementation
return {} as Product;
}
declare const userId: UserId;
declare const productId: ProductId;
getUser(userId); // OK
getProduct(productId); // OK
// getUser(productId); // Error: ProductId is not assignable to UserId
// getProduct(userId); // Error: UserId is not assignable to ProductId
// Creating branded values
function createUserId(id: number): UserId {
return id as UserId;
}
function createProductId(id: number): ProductId {
return id as ProductId;
}
const safeUserId = createUserId(123);
const safeProductId = createProductId(456);Create a branded object type for exact type matching.
type Exact<A extends object> = A & { __brand: keyof A };Usage:
interface User {
id: number;
name: string;
}
type ExactUser = Exact<User>;
// Useful for ensuring exact object shapes
function processExactUser(user: ExactUser): void {
// Implementation
}
// This creates an exact match requirement
const user: ExactUser = { id: 1, name: 'Alice' } as ExactUser;
processExactUser(user);
// Note: This is primarily for advanced type-level programming
// Most applications should use regular TypeScript structural typingExtract the resolved type from a Promise type.
type PromiseType<T extends Promise<any>> = T extends Promise<infer U> ? U : never;Usage:
async function fetchUser(id: number): Promise<{ name: string; email: string }> {
// Implementation
return { name: 'User', email: 'user@example.com' };
}
type UserType = PromiseType<ReturnType<typeof fetchUser>>;
// Result: { name: string; email: string }
// With generic promises
type StringPromise = Promise<string>;
type NumberArrayPromise = Promise<number[]>;
type StringType = PromiseType<StringPromise>; // Result: string
type NumberArrayType = PromiseType<NumberArrayPromise>; // Result: number[]
// Useful for working with async function types
function handleAsyncResult<T extends Promise<any>>(
promise: T,
callback: (result: PromiseType<T>) => void
) {
promise.then(callback);
}Get the union type of all values in an array, object, or array-like structure.
type ValuesType<T extends ReadonlyArray<any> | ArrayLike<any> | Record<any, any>> =
T extends ReadonlyArray<any>
? T[number]
: T extends ArrayLike<any>
? T[number]
: T extends object
? T[keyof T]
: never;Usage:
// With arrays
const colors = ['red', 'green', 'blue'] as const;
type Color = ValuesType<typeof colors>;
// Result: 'red' | 'green' | 'blue'
// With objects
const statusCodes = {
OK: 200,
NOT_FOUND: 404,
SERVER_ERROR: 500
} as const;
type StatusCode = ValuesType<typeof statusCodes>;
// Result: 200 | 404 | 500
// With tuples
type UserTuple = [string, number, boolean];
type TupleValues = ValuesType<UserTuple>;
// Result: string | number | boolean
// Practical example
const eventTypes = {
CLICK: 'click',
FOCUS: 'focus',
BLUR: 'blur'
} as const;
type EventType = ValuesType<typeof eventTypes>;
function handleEvent(type: EventType) {
// type is 'click' | 'focus' | 'blur'
switch (type) {
case 'click':
// Handle click
break;
case 'focus':
// Handle focus
break;
case 'blur':
// Handle blur
break;
}
}Recursively make all properties readonly, including nested objects and arrays.
type DeepReadonly<T> = T extends ((...args: any[]) => any) | Primitive
? T
: T extends _DeepReadonlyArray<infer U>
? _DeepReadonlyArray<U>
: T extends _DeepReadonlyObject<infer V>
? _DeepReadonlyObject<V>
: T;
interface _DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}
type _DeepReadonlyObject<T> = { readonly [P in keyof T]: DeepReadonly<T[P]> };Usage:
interface NestedConfig {
server: {
host: string;
port: number;
ssl: {
enabled: boolean;
cert: string;
key: string;
};
};
database: {
hosts: string[];
credentials: {
username: string;
password: string;
};
};
}
type ReadonlyConfig = DeepReadonly<NestedConfig>;
// All properties at every level become readonly:
// {
// readonly server: {
// readonly host: string;
// readonly port: number;
// readonly ssl: {
// readonly enabled: boolean;
// readonly cert: string;
// readonly key: string;
// };
// };
// readonly database: {
// readonly hosts: readonly string[];
// readonly credentials: {
// readonly username: string;
// readonly password: string;
// };
// };
// }
function processConfig(config: ReadonlyConfig) {
// config.server.host = 'new-host'; // Error: Cannot assign to readonly property
// config.database.hosts.push('new-host'); // Error: hosts is readonly array
console.log(config.server.host); // OK: reading is allowed
}Recursively make all properties required, including nested objects and arrays.
type DeepRequired<T> = T extends (...args: any[]) => any
? T
: T extends any[]
? _DeepRequiredArray<T[number]>
: T extends object
? _DeepRequiredObject<T>
: T;
interface _DeepRequiredArray<T> extends Array<DeepRequired<NonUndefined<T>>> {}
type _DeepRequiredObject<T> = { [P in keyof T]-?: DeepRequired<NonUndefined<T[P]>> };Usage:
interface PartialUser {
id?: number;
profile?: {
name?: string;
email?: string;
settings?: {
theme?: 'light' | 'dark';
notifications?: boolean;
};
};
posts?: Array<{
title?: string;
content?: string;
}>;
}
type CompleteUser = DeepRequired<PartialUser>;
// All properties at every level become required:
// {
// id: number;
// profile: {
// name: string;
// email: string;
// settings: {
// theme: 'light' | 'dark';
// notifications: boolean;
// };
// };
// posts: Array<{
// title: string;
// content: string;
// }>;
// }
function validateCompleteUser(user: unknown): user is CompleteUser {
// Validation logic that ensures all nested properties exist
return true; // Simplified
}Recursively make all properties optional, including nested objects and arrays.
type DeepPartial<T> = { [P in keyof T]?: _DeepPartial<T[P]> };
type _DeepPartial<T> = T extends Function
? T
: T extends Array<infer U>
? _DeepPartialArray<U>
: T extends object
? DeepPartial<T>
: T | undefined;
interface _DeepPartialArray<T> extends Array<_DeepPartial<T>> {}Usage:
interface StrictUser {
id: number;
profile: {
name: string;
email: string;
address: {
street: string;
city: string;
country: string;
};
};
preferences: {
theme: 'light' | 'dark';
language: string;
notifications: {
email: boolean;
push: boolean;
};
};
}
type FlexibleUserUpdate = DeepPartial<StrictUser>;
// All properties at every level become optional:
// {
// id?: number;
// profile?: {
// name?: string;
// email?: string;
// address?: {
// street?: string;
// city?: string;
// country?: string;
// };
// };
// preferences?: {
// theme?: 'light' | 'dark';
// language?: string;
// notifications?: {
// email?: boolean;
// push?: boolean;
// };
// };
// }
function updateUser(id: number, updates: FlexibleUserUpdate) {
// Any combination of nested properties can be provided
}
// Usage examples:
updateUser(1, { profile: { name: 'Alice' } });
updateUser(2, { preferences: { notifications: { email: false } } });
updateUser(3, { profile: { address: { city: 'New York' } } });Recursively remove null and undefined from all properties, including nested structures.
type DeepNonNullable<T> = T extends (...args: any[]) => any
? T
: T extends any[]
? _DeepNonNullableArray<T[number]>
: T extends object
? _DeepNonNullableObject<T>
: T;
interface _DeepNonNullableArray<T> extends Array<DeepNonNullable<NonNullable<T>>> {}
type _DeepNonNullableObject<T> = { [P in keyof T]-?: DeepNonNullable<NonNullable<T[P]>> };Usage:
interface NullableData {
id: number | null;
user: {
name: string | null;
email?: string | null;
profile: {
avatar: string | null | undefined;
bio: string | null;
} | null;
} | null;
tags: Array<string | null> | null;
}
type CleanData = DeepNonNullable<NullableData>;
// All null and undefined are removed at every level:
// {
// id: number;
// user: {
// name: string;
// email: string;
// profile: {
// avatar: string;
// bio: string;
// };
// };
// tags: Array<string>;
// }
function processCleanData(data: CleanData) {
// All properties are guaranteed to be non-null/non-undefined
console.log(data.user.name.toUpperCase()); // No null checks needed
console.log(data.user.profile.avatar.length); // Safe to access
data.tags.forEach(tag => console.log(tag.trim())); // No null in array
}Install with Tessl CLI
npx tessl i tessl/npm-utility-types