20 TypeScript Tips for Cleaner, More Efficient Code in 2024
Add to your RSS feed4 November 20243 min readTable of Contents
TypeScript enhances JavaScript with type safety and powerful features, but many developers use only its basics. Here’s a guide to 20 advanced TypeScript tricks that can boost your productivity, code maintainability, and will help improve code efficiency and readability. Each trick is demonstrated with practical code examples.
1. NonNullable: Excludes null
and undefined
.
1 type User = { name: string; age?: number | null };2 const userAge: NonNullable<User["age"]> = 30; // No null or undefined
2. Partial: Makes all properties optional.
1 interface User { name: string; age: number; email: string; }2 const updateUser = (user: Partial<User>) => ({ ...user, updatedAt: new Date() });3 updateUser({ name: 'John' });
3. Readonly: Enforces immutability.
1 const config: Readonly<{ apiUrl: string; retries: number }> = { apiUrl: 'https://api.com', retries: 5 };2 config.apiUrl = 'https://newapi.com'; // Error
4. Mapped Types: Transform existing types dynamically.
1 type Status = 'loading' | 'success' | 'error';2 type ApiResponse<T> = { [K in Status]: T };3 const response: ApiResponse<string> = { loading: 'Loading...', success: 'Done', error: 'Error' };
5. Optional Tuple Elements: Use variadic tuple types.
1 type UserTuple = [string, number?, boolean?];2 const user1: UserTuple = ['Rob']; // Only name
6. Union Exhaustiveness: Ensure all cases are handled.
1 type Status = 'open' | 'closed' | 'pending';2 function handleStatus(status: Status) {3 switch (status) {4 case 'open': return 'Opened';5 case 'closed': return 'Closed';6 case 'pending': return 'Pending';7 default: const exhaustiveCheck: never = status; return exhaustiveCheck;8 }9 }
7. Omit: Remove properties from a type.
1 interface Todo { title: string; description: string; completed: boolean; }2 type TodoPreview = Omit<Todo, 'description'>;3 const todo: TodoPreview = { title: 'Learn TypeScript', completed: false };
8. Type Narrowing: Use in and instanceof to narrow types.
1 function processInput(input: string | number | { title: string }) {2 if (typeof input === 'string') return input.toUpperCase();3 if ('title' in input) return input.title.toUpperCase();4 }
9. Conditional Types: Apply conditional logic.
1 type IsString<T> = T extends string ? true : false;2 type CheckString = IsString<'Hello'>; // true
10. Literal Types with as const
:
1 const COLORS = ['red', 'green', 'blue'] as const;2 type Color = typeof COLORS[number]; // 'red' | 'green' | 'blue'
11. Extract and Exclude: Filter union types.
1 type T = 'a' | 'b' | 'c';2 type OnlyAOrB = Extract<T, 'a' | 'b'>; // 'a' | 'b'
12. Custom Type Guards:
1 function isString(input: any): input is string { return typeof input === 'string'; }
13. Record: Dynamic object types.
1 type Role = 'admin' | 'user' | 'guest';2 const permissions: Record<Role, string[]> = { admin: ['write'], user: ['read'], guest: ['view'] };
14. Index Signatures: Add dynamic properties.
1 class DynamicObject { [key: string]: any; }2 const obj = new DynamicObject(); obj.name = 'Rob';
15. Never Type: For exhaustive checks.
1 function assertNever(value: never): never { throw new Error(`Unexpected: ${value}`); }
16. Optional Chaining:
1 const user = { profile: { name: 'John' } };2 const userName = user?.profile?.name; // 'John'
17. Null Coalescing (??
):
1 const input: string | null = null;2 const defaultValue = input ?? 'Default'; // 'Default'
18. ReturnType: Infer function return types.
1 function getUser() { return { name: 'John', age: 30 }; }2 type UserReturn = ReturnType<typeof getUser>;
19. Generics: Flexible function types.
1 function identity<T>(value: T): T { return value; }2 identity<string>('Hello'); // 'Hello'
20. Intersection Types: Combine multiple types.
1 type Admin = { privileges: string[] };2 type User = { name: string };3 type AdminUser = Admin & User;
Summary
Each of these tips highlights ways to write cleaner, more reliable TypeScript code. Using these tricks, you can leverage TypeScript’s full type system for safer and more maintainable development.
Would you like me to explain any of these tricks in more detail? I can also provide additional examples or show how these patterns can be combined in real-world scenarios.