TypeScript Utility Types
Built-in type transformation helpers
🛠️ What are Utility Types?
Utility types are built-in TypeScript helpers that transform existing types. They save time by providing common type transformations like making properties optional, readonly, or picking specific properties from types.
type User = { name: string; age: number; email: string };
type PartialUser = Partial<User>; // All properties optional
type UserName = Pick<User, "name">; // Only name property
Common Utility Types
Partial<T>
Makes all properties optional
type User = { name: string; age: number };
type PartialUser = Partial<User>;
Required<T>
Makes all properties required
type Optional = { name?: string };
type Required = Required<Optional>;
Readonly<T>
Makes all properties readonly
type User = { name: string };
type ReadonlyUser = Readonly<User>;
Pick<T, K>
Select specific properties
type User = { name: string; age: number };
type Name = Pick<User, "name">;
🔹 Partial<T>
Makes all properties optional - useful for updates:
type User = {
id: number;
name: string;
email: string;
age: number;
};
type PartialUser = Partial<User>;
// Result: {
// id?: number;
// name?: string;
// email?: string;
// age?: number;
// }
function updateUser(id: number, updates: PartialUser) {
// Update only provided fields
console.log(`Updating user ${id}`, updates);
}
updateUser(1, { name: "Alice" }); // ✓ Valid
updateUser(2, { email: "[email protected]" }); // ✓ Valid
updateUser(3, {}); // ✓ Valid
🔹 Required<T>
Makes all properties required - opposite of Partial:
type OptionalUser = {
name?: string;
email?: string;
age?: number;
};
type RequiredUser = Required<OptionalUser>;
// Result: {
// name: string;
// email: string;
// age: number;
// }
const user1: OptionalUser = { name: "Alice" }; // ✓ Valid
const user2: RequiredUser = { name: "Bob" }; // ✗ Error: missing email, age
const user3: RequiredUser = {
name: "Charlie",
email: "[email protected]",
age: 30
}; // ✓ Valid
🔹 Readonly<T>
Makes all properties immutable:
type Config = {
apiUrl: string;
timeout: number;
retries: number;
};
type ReadonlyConfig = Readonly<Config>;
// Result: {
// readonly apiUrl: string;
// readonly timeout: number;
// readonly retries: number;
// }
const config: ReadonlyConfig = {
apiUrl: "https://api.example.com",
timeout: 5000,
retries: 3
};
console.log(config.apiUrl); // ✓ Can read
// config.timeout = 10000; // ✗ Error: Cannot assign to readonly
🔹 Pick<T, K>
Select specific properties from a type:
type User = {
id: number;
name: string;
email: string;
password: string;
createdAt: Date;
};
type PublicUser = Pick<User, "id" | "name" | "email">;
// Result: {
// id: number;
// name: string;
// email: string;
// }
const publicProfile: PublicUser = {
id: 1,
name: "Alice",
email: "[email protected]"
// password not included - good for API responses!
};
🔹 Omit<T, K>
Remove specific properties from a type:
type User = {
id: number;
name: string;
email: string;
password: string;
};
type UserWithoutPassword = Omit<User, "password">;
// Result: {
// id: number;
// name: string;
// email: string;
// }
type UserWithoutSensitive = Omit<User, "password" | "email">;
// Result: {
// id: number;
// name: string;
// }
const safeUser: UserWithoutPassword = {
id: 1,
name: "Bob",
email: "[email protected]"
};
🔹 Record<K, T>
Create an object type with specific keys and value type:
type Role = "admin" | "user" | "guest";
type Permissions = Record<Role, string[]>;
// Result: {
// admin: string[];
// user: string[];
// guest: string[];
// }
const permissions: Permissions = {
admin: ["read", "write", "delete"],
user: ["read", "write"],
guest: ["read"]
};
// Another example
type PageInfo = Record<string, { title: string; views: number }>;
const pages: PageInfo = {
home: { title: "Home", views: 1000 },
about: { title: "About", views: 500 }
};
🔹 Exclude<T, U> and Extract<T, U>
Filter union types:
type AllTypes = string | number | boolean | null;
// Exclude: Remove types from union
type NoNull = Exclude<AllTypes, null>;
// Result: string | number | boolean
type OnlyPrimitives = Exclude<AllTypes, null | boolean>;
// Result: string | number
// Extract: Keep only matching types
type OnlyStrings = Extract<AllTypes, string>;
// Result: string
type StringOrNumber = Extract<AllTypes, string | number>;
// Result: string | number
🔹 ReturnType<T> and Parameters<T>
Extract types from functions:
function createUser(name: string, age: number) {
return { id: 1, name, age, createdAt: new Date() };
}
// Get return type
type User = ReturnType<typeof createUser>;
// Result: { id: number; name: string; age: number; createdAt: Date }
// Get parameter types
type CreateUserParams = Parameters<typeof createUser>;
// Result: [name: string, age: number]
// Use in practice
function logUser(user: User) {
console.log(user.name, user.age);
}
function callCreateUser(...args: CreateUserParams) {
return createUser(...args);
}
🔹 NonNullable<T>
Remove null and undefined from a type:
type MaybeString = string | null | undefined;
type DefiniteString = NonNullable<MaybeString>;
// Result: string
type MixedTypes = string | number | null | undefined | boolean;
type NoNulls = NonNullable<MixedTypes>;
// Result: string | number | boolean
function processValue(value: MaybeString): NonNullable<MaybeString> {
if (value === null || value === undefined) {
throw new Error("Value cannot be null or undefined");
}
return value; // TypeScript knows it's string here
}
🔹 Practical Example: API Response Handler
Combine utility types for real-world scenarios:
type User = {
id: number;
name: string;
email: string;
password: string;
role: "admin" | "user";
createdAt: Date;
updatedAt: Date;
};
// For API responses (no password)
type UserResponse = Omit<User, "password">;
// For creating users (no id, dates)
type CreateUserInput = Omit<User, "id" | "createdAt" | "updatedAt">;
// For updating users (all optional except id)
type UpdateUserInput = Partial<Omit<User, "id">> & Pick<User, "id">;
// For public profiles (limited info)
type PublicProfile = Pick<User, "id" | "name">;
// Usage
function createUser(input: CreateUserInput): UserResponse {
// Create user logic
return {
id: 1,
name: input.name,
email: input.email,
role: input.role,
createdAt: new Date(),
updatedAt: new Date()
};
}
function updateUser(input: UpdateUserInput): UserResponse {
// Update user logic
return {} as UserResponse;
}
💡 Key Takeaways
- Partial<T> - Makes all properties optional
- Required<T> - Makes all properties required
- Readonly<T> - Makes all properties immutable
- Pick<T, K> - Selects specific properties
- Omit<T, K> - Removes specific properties
- Record<K, T> - Creates object with specific keys
- Combine utilities for complex type transformations