TypeScript has become an essential tool for modern JavaScript development. Let's explore some tips and patterns that will level up your TypeScript skills.
Why TypeScript Matters
TypeScript adds static typing to JavaScript, which means:
- Catch errors at compile time, not runtime
- Better IDE support with autocomplete and refactoring
- Self-documenting code through types
- Safer refactoring
Essential Type Patterns
Union Types
Union types let you express that a value can be one of several types:
type Status = 'pending' | 'approved' | 'rejected';
function processRequest(status: Status) {
// TypeScript knows status can only be one of three values
}
Generics
Generics allow you to write reusable, type-safe code:
function getFirst<T>(array: T[]): T | undefined {
return array[0];
}
const firstNumber = getFirst([1, 2, 3]); // number | undefined
const firstString = getFirst(['a', 'b']); // string | undefined
Type Guards
Type guards help narrow types at runtime:
interface Dog {
bark(): void;
}
interface Cat {
meow(): void;
}
function isDog(pet: Dog | Cat): pet is Dog {
return 'bark' in pet;
}
Advanced Patterns
Utility Types
TypeScript provides powerful utility types:
interface User {
id: number;
name: string;
email: string;
password: string;
}
// Make all properties optional
type PartialUser = Partial<User>;
// Pick specific properties
type UserPreview = Pick<User, 'id' | 'name'>;
// Omit sensitive data
type SafeUser = Omit<User, 'password'>;
Mapped Types
Create new types by transforming existing ones:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Optional<T> = {
[P in keyof T]?: T[P];
};
Best Practices
- Enable strict mode - Use
"strict": truein tsconfig.json - Avoid
any- Useunknownwhen type is truly unknown - Use interfaces for objects - They're more extendable
- Leverage inference - Don't over-annotate obvious types
- Use const assertions -
as constfor literal types
Common Mistakes to Avoid
Don't use any as an escape hatch:
// Bad
const data: any = fetchData();
// Good
const data: unknown = fetchData();
if (isValidData(data)) {
// Now TypeScript knows the type
}
Don't ignore errors:
// Bad
// @ts-ignore
const result = riskyOperation();
// Good
try {
const result = riskyOperation();
} catch (error) {
if (error instanceof SpecificError) {
// Handle appropriately
}
}
Conclusion
TypeScript is more than just "JavaScript with types." It's a powerful tool that, when used correctly, leads to more robust and maintainable code.
Start incorporating these patterns into your projects, and you'll see the benefits immediately!