TypeScript Tips and Tricks
Advanced TypeScript patterns for better code quality and type safety
TypeScript Tips and Tricks
TypeScript has become the standard for building large-scale JavaScript applications. Let's explore some advanced patterns and techniques to write better TypeScript code.
Utility Types
TypeScript provides several built-in utility types:
Partial and Required
interface User {
id: string;
name: string;
email: string;
}
// Make all properties optional
type PartialUser = Partial<User>;
// Make all properties required
type RequiredUser = Required<User>;
Pick and Omit
// Pick specific properties
type UserPreview = Pick<User, 'id' | 'name'>;
// Omit specific properties
type UserWithoutEmail = Omit<User, 'email'>;
Type Guards
Create custom type guards for better type narrowing:
interface Dog {
bark(): void;
}
interface Cat {
meow(): void;
}
function isDog(animal: Dog | Cat): animal is Dog {
return (animal as Dog).bark !== undefined;
}
function makeSound(animal: Dog | Cat) {
if (isDog(animal)) {
animal.bark(); // TypeScript knows it's a Dog
} else {
animal.meow(); // TypeScript knows it's a Cat
}
}
Generic Constraints
Use constraints to make your generics more specific:
interface HasId {
id: string;
}
function findById<T extends HasId>(items: T[], id: string): T | undefined {
return items.find(item => item.id === id);
}
Const Assertions
Use const assertions for literal types:
const colors = ['red', 'green', 'blue'] as const;
type Color = typeof colors[number]; // 'red' | 'green' | 'blue'
Template Literal Types
Create powerful string manipulation types:
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Endpoint = `/api/${string}`;
type APIRoute = `${HTTPMethod} ${Endpoint}`;
// APIRoute = 'GET /api/users' | 'POST /api/users' | ...
Conditional Types
Create types that depend on conditions:
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
Best Practices
- Enable Strict Mode: Always use
"strict": trueintsconfig.json - Avoid
any: Useunknowninstead and narrow the type - Use Type Inference: Let TypeScript infer types when possible
- Create Reusable Types: Define common types in a central location
- Document Complex Types: Add JSDoc comments for clarity
Conclusion
TypeScript's type system is incredibly powerful. By mastering these patterns and techniques, you can write more robust, maintainable code with better tooling support and fewer runtime errors.
Keep exploring and experimenting with TypeScript's features to become a more proficient developer!