TypeScript utility types I use every day — and how to build your own
← Back
April 4, 2026NodeJS7 min read

TypeScript utility types I use every day — and how to build your own

Published April 4, 20267 min read

I used Partial and Omit for years but stopped there. Then I discovered that the built-in utility types are just thin wrappers around mapped types and conditional types — and that I could build my own to solve specific problems. Here are the utility types I reach for daily and how to build the ones the standard library doesn't have.

The essential built-ins with real examples

typescript
interface User {
  id: string;
  email: string;
  name: string;
  role: 'admin' | 'user';
  createdAt: Date;
}

// Partial: all fields optional — perfect for PATCH/update operations
type UserPatch = Partial;

// Required: all fields required — strip optional markers
type RequiredUser = Required;

// Pick: select specific fields
type UserSummary = Pick;
type UserAuth = Pick;

// Omit: exclude specific fields
type UserCreate = Omit; // Remove DB-generated fields
type UserPublic = Omit; // Remove sensitive fields

// Record: build a map type
type UserMap = Record; // { [userId: string]: User }
type RolePermissions = Record; // { admin: [...], user: [...] }

// Extract / Exclude: filter union types
type AdminRole = Extract;    // 'admin'
type NonAdmin = Exclude;     // 'user'

Function utility types

typescript
async function createUser(email: string, name: string, role: string): Promise {
  // ...
}

// Extract parameter types without importing the function's signature separately
type CreateUserParams = Parameters;
// [email: string, name: string, role: string]

// Extract return type
type CreateUserResult = Awaited>;
// User (unwraps Promise)

// Useful for wrapping or proxying functions
function withLogging unknown>(fn: T): T {
  return ((...args: Parameters) => {
    console.log('Calling', fn.name, 'with', args);
    return fn(...args);
  }) as T;
}

const loggedCreate = withLogging(createUser);
// loggedCreate has the same type as createUser

Building custom utility types

typescript
// DeepPartial: partial recursively (standard Partial is shallow)
type DeepPartial = T extends object
  ? { [K in keyof T]?: DeepPartial }
  : T;

interface Config {
  database: { host: string; port: number; credentials: { user: string; pass: string } };
  cache: { ttl: number; maxSize: number };
}

type ConfigOverride = DeepPartial;
// database.credentials.pass is optional at any depth

// Nullable: make all fields nullable
type Nullable = { [K in keyof T]: T[K] | null };

// NonNullable fields only
type NonNullableFields = { [K in keyof T]-?: NonNullable };

// PickByType: select fields of a specific type
type PickByType = {
  [K in keyof T as T[K] extends ValueType ? K : never]: T[K];
};

interface OrderForm {
  productId: string;
  quantity: number;
  discount: number;
  notes: string;
  isGift: boolean;
}

type StringFields = PickByType;
// { productId: string; notes: string }

type NumberFields = PickByType;
// { quantity: number; discount: number }

Path utility type for nested objects

typescript
// Get all dot-separated paths in a nested object
type Paths = {
  [K in keyof T & string]: T[K] extends object
    ? Paths | `${Prefix}${K}`
    : `${Prefix}${K}`;
}[keyof T & string];

interface AppConfig {
  db: { host: string; port: number };
  cache: { ttl: number };
  name: string;
}

type ConfigPaths = Paths;
// "db" | "db.host" | "db.port" | "cache" | "cache.ttl" | "name"

function getConfig(path: ConfigPaths): unknown {
  // path is type-safe — only valid paths allowed
}

getConfig("db.host");    // OK
getConfig("db.port");    // OK
getConfig("db.invalid"); // Error! Not a valid path

Building custom utility types is a skill that pays off in complex codebases. Once you understand that Partial<T> is just { [K in keyof T]?: T[K] }, you can write your own variations. The key is thinking about: what transformation do I want to apply to every key? That becomes your mapped type. What condition determines whether to include a key? That becomes your conditional type.

Share this
← All Posts7 min read