TypeScript provides powerful utility types for type transformations. Here's a comprehensive guide.
Property Modifiers#
1// Partial - all properties optional
2interface User {
3 id: number;
4 name: string;
5 email: string;
6}
7
8type PartialUser = Partial<User>;
9// { id?: number; name?: string; email?: string }
10
11function updateUser(id: number, updates: Partial<User>): User {
12 const user = findUser(id);
13 return { ...user, ...updates };
14}
15
16updateUser(1, { name: 'Alice' }); // Only update name
17
18// Required - all properties required
19interface Config {
20 host?: string;
21 port?: number;
22 ssl?: boolean;
23}
24
25type RequiredConfig = Required<Config>;
26// { host: string; port: number; ssl: boolean }
27
28function initServer(config: RequiredConfig): void {
29 // All properties guaranteed
30}
31
32// Readonly - all properties readonly
33type ReadonlyUser = Readonly<User>;
34// { readonly id: number; readonly name: string; readonly email: string }
35
36const frozenUser: ReadonlyUser = {
37 id: 1,
38 name: 'Alice',
39 email: 'alice@example.com',
40};
41// frozenUser.name = 'Bob'; // Error: readonlyProperty Selection#
1// Pick - select specific properties
2interface Article {
3 id: number;
4 title: string;
5 content: string;
6 author: string;
7 createdAt: Date;
8 updatedAt: Date;
9}
10
11type ArticlePreview = Pick<Article, 'id' | 'title' | 'author'>;
12// { id: number; title: string; author: string }
13
14// Omit - exclude specific properties
15type ArticleInput = Omit<Article, 'id' | 'createdAt' | 'updatedAt'>;
16// { title: string; content: string; author: string }
17
18// Combining Pick and Omit
19type EditableFields = Pick<Article, 'title' | 'content'>;
20type ArticleWithMeta = Omit<Article, 'content'>;Key Types#
1// Record - create object type with specific keys
2type PageNames = 'home' | 'about' | 'contact';
3type PageConfig = Record<PageNames, { title: string; path: string }>;
4
5const pages: PageConfig = {
6 home: { title: 'Home', path: '/' },
7 about: { title: 'About', path: '/about' },
8 contact: { title: 'Contact', path: '/contact' },
9};
10
11// Dynamic keys
12type UserRoles = Record<string, string[]>;
13const permissions: UserRoles = {
14 admin: ['read', 'write', 'delete'],
15 user: ['read'],
16};
17
18// keyof - get keys as union
19type UserKeys = keyof User;
20// 'id' | 'name' | 'email'
21
22function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
23 return obj[key];
24}Exclusion Types#
1// Exclude - exclude types from union
2type AllTypes = string | number | boolean | null | undefined;
3type PrimitiveTypes = Exclude<AllTypes, null | undefined>;
4// string | number | boolean
5
6type Status = 'pending' | 'active' | 'completed' | 'cancelled';
7type ActiveStatus = Exclude<Status, 'pending' | 'cancelled'>;
8// 'active' | 'completed'
9
10// Extract - extract matching types
11type TextTypes = Extract<AllTypes, string | number>;
12// string | number
13
14type PendingOrActive = Extract<Status, 'pending' | 'active'>;
15// 'pending' | 'active'
16
17// NonNullable - exclude null and undefined
18type MaybeString = string | null | undefined;
19type DefiniteString = NonNullable<MaybeString>;
20// stringFunction Types#
1// Parameters - get function parameter types
2function createUser(name: string, age: number, admin: boolean): User {
3 // ...
4}
5
6type CreateUserParams = Parameters<typeof createUser>;
7// [string, number, boolean]
8
9// ReturnType - get function return type
10type CreateUserReturn = ReturnType<typeof createUser>;
11// User
12
13// Async function return type
14async function fetchUser(id: number): Promise<User> {
15 // ...
16}
17
18type FetchReturn = ReturnType<typeof fetchUser>;
19// Promise<User>
20
21type AwaitedReturn = Awaited<ReturnType<typeof fetchUser>>;
22// User
23
24// ConstructorParameters
25class MyClass {
26 constructor(name: string, value: number) {}
27}
28
29type MyClassParams = ConstructorParameters<typeof MyClass>;
30// [string, number]
31
32// InstanceType
33type MyClassInstance = InstanceType<typeof MyClass>;
34// MyClassString Manipulation#
1// Uppercase
2type Shouting = Uppercase<'hello'>;
3// 'HELLO'
4
5// Lowercase
6type Quiet = Lowercase<'HELLO'>;
7// 'hello'
8
9// Capitalize
10type Titled = Capitalize<'hello'>;
11// 'Hello'
12
13// Uncapitalize
14type Untitled = Uncapitalize<'Hello'>;
15// 'hello'
16
17// Practical example: event handlers
18type EventName = 'click' | 'focus' | 'blur';
19type HandlerName = `on${Capitalize<EventName>}`;
20// 'onClick' | 'onFocus' | 'onBlur'Awaited Type#
1// Unwrap Promise types
2type Promised = Promise<string>;
3type Resolved = Awaited<Promised>;
4// string
5
6// Nested promises
7type NestedPromise = Promise<Promise<number>>;
8type DeepResolved = Awaited<NestedPromise>;
9// number
10
11// With union types
12type MixedPromise = Promise<string> | number;
13type MixedResolved = Awaited<MixedPromise>;
14// string | number
15
16// Practical usage
17async function processData(): Promise<Data> {
18 // ...
19}
20
21type DataType = Awaited<ReturnType<typeof processData>>;
22// DataThisType#
1// Define 'this' context for object literals
2interface Methods {
3 greet(): string;
4 getName(): string;
5}
6
7interface Data {
8 name: string;
9}
10
11const obj: Methods & ThisType<Data & Methods> = {
12 greet() {
13 return `Hello, ${this.getName()}`;
14 },
15 getName() {
16 return this.name;
17 },
18};
19
20// Vue-style options
21interface ComponentOptions<D, M> {
22 data(): D;
23 methods: M & ThisType<D & M>;
24}
25
26function defineComponent<D, M>(options: ComponentOptions<D, M>): D & M {
27 // ...
28}Combining Utility Types#
1// Complex type transformations
2interface Product {
3 id: number;
4 name: string;
5 price: number;
6 description: string;
7 category: string;
8 inStock: boolean;
9}
10
11// Partial update excluding id
12type ProductUpdate = Partial<Omit<Product, 'id'>>;
13
14// Required subset
15type RequiredProductInfo = Required<Pick<Product, 'name' | 'price'>>;
16
17// Readonly response
18type ProductResponse = Readonly<Product>;
19
20// Record of products by id
21type ProductCatalog = Record<string, Product>;
22
23// Pick and make readonly
24type ProductSummary = Readonly<Pick<Product, 'id' | 'name' | 'price'>>;Custom Utility Types#
1// Deep Partial
2type DeepPartial<T> = T extends object
3 ? { [P in keyof T]?: DeepPartial<T[P]> }
4 : T;
5
6interface NestedConfig {
7 server: {
8 host: string;
9 port: number;
10 ssl: {
11 enabled: boolean;
12 cert: string;
13 };
14 };
15}
16
17type PartialConfig = DeepPartial<NestedConfig>;
18
19// Deep Readonly
20type DeepReadonly<T> = T extends object
21 ? { readonly [P in keyof T]: DeepReadonly<T[P]> }
22 : T;
23
24// Mutable (remove readonly)
25type Mutable<T> = {
26 -readonly [P in keyof T]: T[P];
27};
28
29// Optional keys
30type OptionalKeys<T> = {
31 [K in keyof T]-?: undefined extends T[K] ? K : never;
32}[keyof T];
33
34// Required keys
35type RequiredKeys<T> = {
36 [K in keyof T]-?: undefined extends T[K] ? never : K;
37}[keyof T];
38
39// Function properties only
40type FunctionProperties<T> = {
41 [K in keyof T]: T[K] extends Function ? K : never;
42}[keyof T];Practical Examples#
1// Form handling
2interface FormFields {
3 username: string;
4 email: string;
5 password: string;
6}
7
8type FormErrors = Partial<Record<keyof FormFields, string>>;
9type FormTouched = Partial<Record<keyof FormFields, boolean>>;
10
11interface FormState {
12 values: FormFields;
13 errors: FormErrors;
14 touched: FormTouched;
15}
16
17// API responses
18interface ApiResponse<T> {
19 data: T;
20 status: number;
21 message: string;
22}
23
24type UserResponse = ApiResponse<User>;
25type UsersResponse = ApiResponse<User[]>;
26
27// Event handling
28type EventHandlers<T> = {
29 [K in keyof T as `on${Capitalize<string & K>}`]: (value: T[K]) => void;
30};
31
32type UserEvents = EventHandlers<User>;
33// { onId: (value: number) => void; onName: (value: string) => void; ... }Best Practices#
Selection:
✓ Use Pick for whitelist approach
✓ Use Omit for blacklist approach
✓ Combine for complex transformations
✓ Document type intentions
Modifiers:
✓ Use Partial for optional updates
✓ Use Required for strict validation
✓ Use Readonly for immutability
✓ Consider deep versions for nested types
Functions:
✓ Use Parameters for argument types
✓ Use ReturnType for return types
✓ Use Awaited for async functions
✓ Create type-safe wrappers
Custom Types:
✓ Build on built-in utilities
✓ Use conditional types for flexibility
✓ Document complex type transformations
✓ Test with various input types
Conclusion#
TypeScript's utility types enable powerful type transformations. Master the built-in types for common patterns, combine them for complex transformations, and create custom utilities for project-specific needs. These tools make TypeScript's type system incredibly flexible and expressive.