Advanced TypeScript Patterns for Better Code Quality
10 min read
TypeScript
Programming
Best Practices
Advanced TypeScript Patterns for Better Code Quality
TypeScript's type system is incredibly powerful. Let's explore some advanced patterns that can significantly improve your code quality.
Discriminated Unions
Discriminated unions help create type-safe state machines:
type LoadingState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: string }
| { status: 'error'; error: Error };
function handleState(state: LoadingState) {
switch (state.status) {
case 'success':
// TypeScript knows state.data exists here
console.log(state.data);
break;
case 'error':
// TypeScript knows state.error exists here
console.error(state.error);
break;
}
}
Conditional Types
Conditional types enable type transformations based on conditions:
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// Practical example: Extract promise type
type Awaited<T> = T extends Promise<infer U> ? U : T;
type Result = Awaited<Promise<string>>; // string
Type Guards
Custom type guards provide runtime type checking:
interface User {
id: string;
name: string;
}
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value
);
}
function processData(data: unknown) {
if (isUser(data)) {
// TypeScript knows data is User here
console.log(data.name);
}
}
Mapped Types
Transform existing types into new ones:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
interface User {
id: string;
name: string;
email: string;
}
type ReadonlyUser = Readonly<User>;
type PartialUser = Partial<User>;
Conclusion
These advanced TypeScript patterns help catch errors at compile time and make your code more maintainable. Start incorporating them into your projects to level up your TypeScript skills.