CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-utility-types

Utility Types Collection for TypeScript providing comprehensive type-level utilities for static type manipulation.

Pending
Overview
Eval results
Files

mapped-types.mddocs/

Advanced Mapped Types

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

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');

Set Operations

SetIntersection

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> | () => any

SetDifference

Get 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'

SetComplement

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();
  }
}

SymmetricDifference

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'

Object Manipulation

Omit

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 }

Optional

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;
}

Required (AugmentedRequired)

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}`);
}

Diff

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; }

PickByValue

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;
}

OmitByValue

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; }

Key Selection

FunctionKeys

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;
// }

NonFunctionKeys

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; }

RequiredKeys

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
}

OptionalKeys

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'
  };
}

Advanced Utilities

UnionToIntersection

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; }

Brand

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);

Exact

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 typing

PromiseType

Extract 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);
}

ValuesType

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;
  }
}

Deep Transformations

DeepReadonly

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
}

DeepRequired

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
}

DeepPartial

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' } } });

DeepNonNullable

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

docs

aliases-guards.md

flow-utilities.md

index.md

mapped-types.md

tile.json