Built-in Utility Types
Partial<T>
Makes all properties optional.
typescript
interface User {
id: number;
name: string;
email: string;
age: number;
}
type PartialUser = Partial<User>;
// {
// id?: number;
// name?: string;
// email?: string;
// age?: number;
// }
function updateUser(id: number, updates: Partial<User>) {
// Merge updates with existing user
return { ...existingUser, ...updates };
}
updateUser(1, { age: 25 }); // Only update ageRequired<T>
Makes all properties required.
typescript
interface PartialUser {
id?: number;
name?: string;
email?: string;
}
type CompleteUser = Required<PartialUser>;
// {
// id: number;
// name: string;
// email: string;
// }Readonly<T>
Makes all properties readonly.
typescript
interface Config {
apiUrl: string;
timeout: number;
}
type ReadonlyConfig = Readonly<Config>;
const config: ReadonlyConfig = {
apiUrl: 'https://api.example.com',
timeout: 5000,
};
// config.apiUrl = 'new url'; // Error: Cannot assignRecord<K, T>
Creates an object type with specified keys and values.
typescript
type UserMap = Record<string, User>;
const users: UserMap = {
user1: { id: 1, name: 'John' },
user2: { id: 2, name: 'Jane' },
};
// More specific keys
type Roles = Record<'admin' | 'user' | 'guest', number>;
const permissions: Roles = {
admin: 7,
user: 4,
guest: 1,
};Pick<T, K>
Selects specific properties from a type.
typescript
interface User {
id: number;
name: string;
email: string;
age: number;
}
type UserBasicInfo = Pick<User, 'id' | 'name'>;
// {
// id: number;
// name: string;
// }
function getUserInfo(user: User): UserBasicInfo {
const { id, name } = user;
return { id, name };
}Omit<T, K>
Removes specific properties from a type.
typescript
interface User {
id: number;
name: string;
email: string;
password: string;
}
type PublicUser = Omit<User, 'password'>;
// {
// id: number;
// name: string;
// email: string;
// }
function toPublicUser(user: User): PublicUser {
const { password, ...publicUser } = user;
return publicUser;
}Exclude<T, U>
Removes types from a union.
typescript
type Primitives = string | number | boolean | null | undefined;
type NonNullable = Exclude<Primitives, null | undefined>;
// string | number | boolean
type OnlyStrings = Exclude<string | number | boolean, number | boolean>;
// stringExtract<T, U>
Extracts types from a union.
typescript
type All = string | number | boolean | Function;
type FunctionsOnly = Extract<All, Function>;
// Function
type StringsAndNumbers = Extract<All, string | number>;
// string | numberNonNullable<T>
Removes null and undefined from a type.
typescript
type Value = string | null | undefined;
type NonNullValue = NonNullable<Value>;
// string
function process(value: NonNullValue) {
// value is guaranteed not to be null or undefined
return value.toUpperCase();
}ReturnType<T>
Extracts the return type of a function type.
typescript
function getUser(): {
id: number;
name: string;
} {
return { id: 1, name: 'John' };
}
type UserReturn = ReturnType<typeof getUser>;
// {
// id: number;
// name: string;
// }
async function fetchData(): Promise<number> {
return 42;
}
type DataReturn = ReturnType<typeof fetchData>;
// Promise<number>InstanceType<T>
Extracts the instance type of a class.
typescript
class User {
constructor(public name: string, public age: number) {}
}
type UserInstance = InstanceType<typeof User>;
// User
function createUser(Class: new (...args: any) => any) {
return new Class();
}
const user = createUser(User); // UserCustom Utility Types
DeepPartial
typescript
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object
? DeepPartial<T[P]>
: T[P];
};
interface Config {
database: {
host: string;
port: number;
credentials: {
username: string;
password: string;
};
};
}
type PartialConfig = DeepPartial<Config>;
// Can partially specify nested propertiesDeepRequired
typescript
type DeepRequired<T> = {
[P in keyof T]-?: T[P] extends object
? DeepRequired<T[P]>
: T[P];
};DeepReadonly
typescript
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? DeepReadonly<T[P]>
: T[P];
};
interface State {
user: {
name: string;
preferences: {
theme: string;
};
};
}
type ReadonlyState = DeepReadonly<State>;
// All nested properties are readonlyMutable
typescript
type Mutable<T> = {
-readonly [P in keyof T]: T[P];
};
interface ReadonlyInterface {
readonly id: number;
readonly name: string;
}
type MutableInterface = Mutable<ReadonlyInterface>;
// {
// id: number;
// name: string;
// }Tuple Operations
typescript
type Push<T extends any[], V> = [...T, V];
type Unshift<T extends any[], V> = [V, ...T];
type Tuple = [1, 2];
type WithPush = Push<Tuple, 3>; // [1, 2, 3]
type WithUnshift = Unshift<Tuple, 0>; // [0, 1, 2]
type First<T extends any[]> = T extends [infer F, ...any[]] ? F : never;
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never;
type Head = First<[1, 2, 3]>; // 1
type Tail = Last<[1, 2, 3]>; // 3Function Types
typescript
type Arguments<T> = T extends (...args: infer A) => any ? A : never;
function example(name: string, age: number): boolean {
return true;
}
type ExampleArgs = Arguments<typeof example>;
// [string, number]Practical Examples
API Response Types
typescript
interface User {
id: number;
name: string;
email: string;
}
// Create user - only provide required fields
type CreateUserInput = Omit<User, 'id'>;
// Update user - all fields optional
type UpdateUserInput = Partial<CreateUserInput>;
// Public user - hide sensitive fields
type PublicUser = Omit<User, 'email' | 'password'>;
async function createUser(input: CreateUserInput): Promise<PublicUser> {
const user = await db.create(input);
const { email, ...publicUser } = user;
return publicUser;
}Form Handling
typescript
interface FormData {
username: string;
email: string;
password: string;
confirmPassword: string;
}
// Form state - all fields optional
type FormState = Partial<FormData>;
// Form errors - all fields optional string
type FormErrors = {
[K in keyof FormData]?: string;
};
const errors: FormErrors = {
username: 'Username is required',
email: 'Invalid email',
};Configuration Types
typescript
interface DatabaseConfig {
host: string;
port: number;
username: string;
password: string;
}
interface ServerConfig {
port: number;
database: DatabaseConfig;
}
// Partial config for development
type DevConfig = Partial<ServerConfig>;
// Required config for production
type ProdConfig = Required<ServerConfig>;Best Practices
- 1Compose utility types: Combine multiple utility types
- 2Create custom utilities: For reusable patterns
- 3Use type inference: Let TypeScript infer when possible
- 4Prefer utility types: Over manual type definitions
- 5Document complex types: Help other developers understand
Conclusion
Utility types provide powerful tools for type manipulation. Use them to write cleaner, more maintainable TypeScript code.