Utility Types and Built-in Type Helpers
1. Partial<T> and Required<T> for Property Modification
| Utility Type | Syntax | Description | Use Case |
|---|---|---|---|
| Partial<T> | Partial<Type> |
Makes all properties optional - adds ? to each property |
Updates, patches, partial configs |
| Required<T> | Required<Type> |
Makes all properties required - removes ? from properties |
Validation, complete objects |
| Readonly<T> | Readonly<Type> |
Makes all properties readonly - adds readonly modifier |
Immutable data, constants |
Example: Partial<T> for optional properties
interface User {
id: number;
name: string;
email: string;
age: number;
}
// All properties optional
type PartialUser = Partial<User>;
// Result: { id?: number; name?: string; email?: string; age?: number; }
// Update function accepts partial data
function updateUser(id: number, updates: Partial<User>): User {
const user = getUserById(id);
return { ...user, ...updates };
}
updateUser(1, { name: "Alice" }); // OK
updateUser(2, { email: "alice@email.com" }); // OK
updateUser(3, {}); // OK - no updates
// Partial for default configs
interface Config {
host: string;
port: number;
ssl: boolean;
timeout: number;
}
function createConfig(overrides: Partial<Config> = {}): Config {
const defaults: Config = {
host: "localhost",
port: 3000,
ssl: false,
timeout: 5000
};
return { ...defaults, ...overrides };
}
Example: Required<T> to enforce completeness
interface Settings {
theme?: "light" | "dark";
fontSize?: number;
notifications?: boolean;
}
// Make all required for validation
type CompleteSettings = Required<Settings>;
// Result: { theme: "light" | "dark"; fontSize: number; notifications: boolean; }
function validateSettings(settings: CompleteSettings): boolean {
// All properties guaranteed to exist
return settings.theme !== undefined &&
settings.fontSize > 0 &&
settings.notifications !== undefined;
}
// Readonly for immutable state
const config: Readonly<Config> = {
host: "localhost",
port: 3000,
ssl: false,
timeout: 5000
};
// config.port = 8080; // Error: Cannot assign to readonly property
2. Pick<T, K> and Omit<T, K> for Property Selection
| Utility Type | Syntax | Description | Use Case |
|---|---|---|---|
| Pick<T, K> | Pick<Type, Keys> |
Select specific properties from type - creates subset | DTOs, API responses, projections |
| Omit<T, K> | Omit<Type, Keys> |
Remove specific properties from type - creates type without keys | Exclude sensitive data, inheritance |
Example: Pick<T, K> to select properties
interface User {
id: number;
name: string;
email: string;
password: string;
role: string;
createdAt: Date;
updatedAt: Date;
}
// Pick only public-facing fields
type PublicUser = Pick<User, "id" | "name" | "email">;
// Result: { id: number; name: string; email: string; }
// Use for API responses
function getPublicProfile(userId: number): PublicUser {
const user = findUser(userId);
return {
id: user.id,
name: user.name,
email: user.email
};
}
// Pick for form fields
type LoginForm = Pick<User, "email" | "password">;
// Result: { email: string; password: string; }
// Pick with union
type UserIdentifiers = Pick<User, "id" | "email">;
// Pick for timestamps
type Timestamps = Pick<User, "createdAt" | "updatedAt">;
Example: Omit<T, K> to exclude properties
// Omit sensitive fields
type SafeUser = Omit<User, "password">;
// Result: All User fields except password
// Omit multiple fields
type UserWithoutMeta = Omit<User, "createdAt" | "updatedAt">;
// Create input type by omitting generated fields
type CreateUserInput = Omit<User, "id" | "createdAt" | "updatedAt">;
// Result: { name: string; email: string; password: string; role: string; }
function createUser(input: CreateUserInput): User {
return {
...input,
id: generateId(),
createdAt: new Date(),
updatedAt: new Date()
};
}
// Omit for derived types
interface Product {
id: string;
name: string;
price: number;
discount: number;
}
type ProductInput = Omit<Product, "id">;
type ProductWithoutDiscount = Omit<Product, "discount">;
3. Record<K, T> and keyof Type Operators
| Feature | Syntax | Description | Use Case |
|---|---|---|---|
| Record<K, T> | Record<Keys, Type> |
Create object type with specified keys and value type | Dictionaries, maps, lookup tables |
| keyof | keyof Type |
Union of all property keys as string/number literals | Type-safe property access, mapping |
| typeof | typeof value |
Extract type from value - works on values, not types | Derive types from objects/functions |
Example: Record<K, T> for dictionaries
// Basic Record
type UserRoles = "admin" | "user" | "guest";
type Permissions = Record<UserRoles, string[]>;
const permissions: Permissions = {
admin: ["read", "write", "delete"],
user: ["read", "write"],
guest: ["read"]
};
// Record with string keys
type StringMap = Record<string, number>;
const scores: StringMap = {
"Alice": 95,
"Bob": 87,
"Charlie": 92
};
// Record with template literal keys
type HTTPMethods = "GET" | "POST" | "PUT" | "DELETE";
type RouteHandlers = Record<HTTPMethods, (req: Request) => Response>;
const handlers: RouteHandlers = {
GET: (req) => ({ status: 200, body: "GET" }),
POST: (req) => ({ status: 201, body: "POST" }),
PUT: (req) => ({ status: 200, body: "PUT" }),
DELETE: (req) => ({ status: 204, body: "" })
};
// Record for configuration
type Environment = "development" | "staging" | "production";
type EnvConfig = Record<Environment, { apiUrl: string; debug: boolean }>;
const config: EnvConfig = {
development: { apiUrl: "http://localhost:3000", debug: true },
staging: { apiUrl: "https://staging.api.com", debug: true },
production: { apiUrl: "https://api.com", debug: false }
};
Example: keyof for type-safe property access
interface Person {
name: string;
age: number;
email: string;
}
// keyof extracts property keys
type PersonKeys = keyof Person;
// Result: "name" | "age" | "email"
// Type-safe property getter
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person: Person = { name: "Alice", age: 30, email: "alice@email.com" };
let name = getProperty(person, "name"); // string
let age = getProperty(person, "age"); // number
// let x = getProperty(person, "invalid"); // Error!
// Type-safe property setter
function setProperty<T, K extends keyof T>(
obj: T,
key: K,
value: T[K]
): void {
obj[key] = value;
}
setProperty(person, "name", "Bob"); // OK
setProperty(person, "age", 25); // OK
// setProperty(person, "age", "25"); // Error: wrong type
// keyof with mapped types
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type PersonGetters = Getters<Person>;
// Result: { getName: () => string; getAge: () => number; getEmail: () => string; }
4. Exclude<T, U> and Extract<T, U> for Union Manipulation
| Utility Type | Syntax | Description | Result |
|---|---|---|---|
| Exclude<T, U> | Exclude<Union, Excluded> |
Remove types from union - filters out specified types | Union without excluded types |
| Extract<T, U> | Extract<Union, Extracted> |
Keep only specified types from union - filters to match | Union containing only extracted types |
| NonNullable<T> | NonNullable<Type> |
Remove null and undefined from type | Type without null/undefined |
Example: Exclude<T, U> to remove types
// Basic Exclude
type AllTypes = string | number | boolean | null | undefined;
type PrimitiveTypes = Exclude<AllTypes, null | undefined>;
// Result: string | number | boolean
// Exclude specific values
type Status = "pending" | "active" | "completed" | "cancelled";
type ActiveStatus = Exclude<Status, "cancelled">;
// Result: "pending" | "active" | "completed"
// Exclude function types
type MixedTypes = string | number | (() => void) | { id: number };
type NonFunctionTypes = Exclude<MixedTypes, Function>;
// Result: string | number | { id: number }
// Exclude keys from type
type UserKeys = keyof User;
type NonIdKeys = Exclude<UserKeys, "id">;
// Practical: exclude built-in methods
type OwnProperties<T> = Exclude<keyof T, keyof any[]>;
// Remove optional properties
type RequiredKeys<T> = Exclude<{
[K in keyof T]-?: {} extends Pick<T, K> ? never : K;
}[keyof T], undefined>;
Example: Extract<T, U> to filter types
// Basic Extract
type AllTypes = string | number | boolean | null;
type NullableTypes = Extract<AllTypes, null | undefined>;
// Result: null
type StringOrNumber = Extract<AllTypes, string | number>;
// Result: string | number
// Extract specific literal types
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
type ReadMethods = Extract<HTTPMethod, "GET">;
// Result: "GET"
type WriteMethods = Extract<HTTPMethod, "POST" | "PUT" | "DELETE" | "PATCH">;
// Result: "POST" | "PUT" | "DELETE" | "PATCH"
// Extract function types
type Mixed = string | number | ((x: number) => number) | ((y: string) => string);
type Functions = Extract<Mixed, Function>;
// Result: ((x: number) => number) | ((y: string) => string)
// Extract by structure
type Shapes =
| { kind: "circle"; radius: number }
| { kind: "square"; size: number }
| { kind: "rectangle"; width: number; height: number };
type CircleShape = Extract<Shapes, { kind: "circle" }>;
// Result: { kind: "circle"; radius: number }
// NonNullable - remove null and undefined
type MaybeString = string | null | undefined;
type DefiniteString = NonNullable<MaybeString>;
// Result: string
5. ReturnType<T> and Parameters<T> for Function Types
| Utility Type | Syntax | Description | Extracts |
|---|---|---|---|
| ReturnType<T> | ReturnType<Function> |
Extract return type from function type | Function return type |
| Parameters<T> | Parameters<Function> |
Extract parameter types as tuple | Parameter tuple type |
| ConstructorParameters<T> | ConstructorParameters<Class> |
Extract constructor parameter types | Constructor params tuple |
| InstanceType<T> | InstanceType<Constructor> |
Extract instance type from constructor | Instance type |
| ThisParameterType<T> | ThisParameterType<Fn> |
Extract this parameter type from function | this type or unknown |
Example: ReturnType<T> for type inference
// Basic ReturnType
function getUser() {
return { id: 1, name: "Alice", email: "alice@email.com" };
}
type User = ReturnType<typeof getUser>;
// Result: { id: number; name: string; email: string; }
// ReturnType with generic function
function fetchData<T>(url: string): Promise<T> {
return fetch(url).then(res => res.json());
}
type FetchReturn = ReturnType<typeof fetchData>;
// Result: Promise<unknown> (generic resolved to unknown)
// ReturnType from function type
type Handler = (event: Event) => void;
type HandlerReturn = ReturnType<Handler>;
// Result: void
// Complex function return
function getConfig() {
return {
api: { url: "https://api.com", timeout: 5000 },
features: { darkMode: true, notifications: false }
} as const;
}
type Config = ReturnType<typeof getConfig>;
// Infers readonly structure
// Array method return types
type MapReturn = ReturnType<typeof Array.prototype.map>;
type FilterReturn = ReturnType<typeof Array.prototype.filter>;
Example: Parameters<T> for parameter types
// Basic Parameters
function createUser(name: string, age: number, email: string) {
return { name, age, email };
}
type CreateUserParams = Parameters<typeof createUser>;
// Result: [name: string, age: number, email: string]
// Use extracted parameters
function logCreateUser(...args: CreateUserParams) {
console.log("Creating user:", args);
return createUser(...args);
}
// Parameters with function type
type HandlerFn = (event: MouseEvent, options: { passive: boolean }) => void;
type HandlerParams = Parameters<HandlerFn>;
// Result: [event: MouseEvent, options: { passive: boolean }]
// Extract specific parameter
type FirstParam<T extends (...args: any[]) => any> = Parameters<T>[0];
type Handler = (id: number, data: string) => void;
type FirstArg = FirstParam<Handler>; // number
// Combine with ReturnType
function transform(input: string): number {
return parseInt(input);
}
type TransformParams = Parameters<typeof transform>; // [string]
type TransformReturn = ReturnType<typeof transform>; // number
6. NonNullable<T> and InstanceType<T> Helpers
| Utility Type | Syntax | Description | Use Case |
|---|---|---|---|
| NonNullable<T> | NonNullable<Type> |
Remove null and undefined from type | Validation, type narrowing |
| InstanceType<T> | InstanceType<Constructor> |
Get instance type from constructor function | Factory patterns, DI containers |
| Awaited<T> TS 4.5+ | Awaited<Promise> |
Recursively unwrap Promise types | Async function return types |
Example: NonNullable<T> for type safety
// Basic NonNullable
type MaybeString = string | null | undefined;
type DefiniteString = NonNullable<MaybeString>;
// Result: string
// NonNullable with union
type Value = string | number | null | undefined | boolean;
type NonNullValue = NonNullable<Value>;
// Result: string | number | boolean
// Filter array to non-null values
function filterNulls<T>(arr: (T | null | undefined)[]): NonNullable<T>[] {
return arr.filter((item): item is NonNullable<T> => item != null);
}
const mixed = [1, null, 2, undefined, 3];
const numbers = filterNulls(mixed); // number[]
// Safe access with NonNullable
type User = { name: string; email?: string | null };
function getEmail(user: User): NonNullable<User["email"]> | "no-email" {
return user.email ?? "no-email";
}
// NonNullable in generic constraints
function process<T>(value: NonNullable<T>): void {
// value is guaranteed not null/undefined
console.log(value);
}
Example: InstanceType<T> for constructor types
// Basic InstanceType
class User {
constructor(public name: string, public age: number) {}
}
type UserInstance = InstanceType<typeof User>;
// Result: User
// Factory function with InstanceType
function createInstance<T extends new (...args: any[]) => any>(
constructor: T,
...args: ConstructorParameters<T>
): InstanceType<T> {
return new constructor(...args);
}
const user = createInstance(User, "Alice", 30); // User
// InstanceType with abstract classes
abstract class Animal {
abstract makeSound(): void;
}
class Dog extends Animal {
makeSound() { console.log("Woof!"); }
}
type DogInstance = InstanceType<typeof Dog>; // Dog
// Registry pattern
class Registry {
private constructors = new Map<string, new (...args: any[]) => any>();
register<T extends new (...args: any[]) => any>(
name: string,
constructor: T
): void {
this.constructors.set(name, constructor);
}
create<T extends new (...args: any[]) => any>(
name: string,
...args: any[]
): InstanceType<T> | undefined {
const Constructor = this.constructors.get(name) as T;
return Constructor ? new Constructor(...args) : undefined;
}
}
Example: Awaited<T> for Promise unwrapping
// Awaited unwraps Promise types
type StringPromise = Promise<string>;
type UnwrappedString = Awaited<StringPromise>;
// Result: string
// Deep unwrapping
type NestedPromise = Promise<Promise<number>>;
type UnwrappedNumber = Awaited<NestedPromise>;
// Result: number
// Awaited with async function
async function fetchUser(): Promise<{ id: number; name: string }> {
return { id: 1, name: "Alice" };
}
type FetchedUser = Awaited<ReturnType<typeof fetchUser>>;
// Result: { id: number; name: string }
// Non-Promise types pass through
type NotPromise = Awaited<string>;
// Result: string
// Awaited with union
type MixedPromises = Promise<string> | Promise<number> | boolean;
type UnwrappedMixed = Awaited<MixedPromises>;
// Result: string | number | boolean