5 TypeScript Features That Will Make Your Code More Robust
Erik Nguyen / November 20, 2024
5 TypeScript Features That Will Make Your Code More Robust
TypeScript continues to be a game-changer for developers seeking more reliable and maintainable code. In this post, we'll explore five powerful features that can significantly improve your code's robustness and type safety.
1. Discriminated Unions: Precise Type Narrowing
Discriminated unions allow you to create more precise type checks and safer code patterns:
type Result =
| { type: 'success'; data: string }
| { type: 'error'; message: string };
function handleResult(result: Result) {
switch (result.type) {
case 'success':
// TypeScript knows `data` is available
console.log(result.data.toUpperCase());
break;
case 'error':
// TypeScript knows `message` is available
console.error(result.message);
break;
}
}
Pro Tip: Discriminated unions provide compile-time safety by ensuring exhaustive type checking and preventing runtime errors.
2. Conditional Types: Dynamic Type Manipulation
Conditional types enable incredibly flexible type transformations:
type NonNullable = T extends null | undefined ? never : T;
type Example = NonNullable;
// Results in type 'string'
type ExtractArray = T extends any[] ? T[number] : T;
type NumberArray = ExtractArray; // type is 'number'
type SingleNumber = ExtractArray; // type remains 'number'
Advanced Technique: Conditional types allow you to create powerful type-level utilities that can dramatically reduce type-related boilerplate.
3. Mapped Types: Transform Existing Types
Mapped types let you create new types by transforming existing ones:
interface Product {
name: string;
price: number;
description: string;
}
// Make all properties optional
type PartialProduct = {
[P in keyof Product]?: Product[P];
};
// Make all properties readonly
type ReadonlyProduct = {
readonly [P in keyof Product]: Product[P];
};
Design Pattern: Mapped types are crucial for creating flexible type transformations without duplicating type definitions.
4. Branded Types: Prevent Accidental Type Misuse
Create type-safe primitives that prevent incorrect type assignments:
type Brand = K & { __brand: T };
type USD = Brand;
type EUR = Brand;
function convertUSD(amount: USD) {
return amount;
}
const dollars = 100 as USD;
const euros = 100 as EUR;
// Compile-time error: Cannot pass euros where USD is expected
convertUSD(euros);
Type Safety: Branded types provide an extra layer of type checking to prevent logical errors in your code.
5. Const Assertions: Literal Type Precision
Use const assertions to create more precise type definitions:
const colors = {
primary: '#007bff',
secondary: '#6c757d',
success: '#28a745',
} as const;
// Exact literal types, not just 'string'
type Color = (typeof colors)[keyof typeof colors];
function setColor(color: Color) {
// Strictly typed color assignment
}
// Only these exact colors are allowed
setColor(colors.primary);
Precision Matters: Const assertions lock down types to their exact literal values, providing unprecedented type precision.
Conclusion
TypeScript's advanced type system offers powerful tools to write more robust, self-documenting code. By leveraging these features, you can catch errors earlier, reduce runtime bugs, and create more maintainable applications.
Remember, type safety is a journey. Start small, experiment, and gradually incorporate these techniques into your workflow.
Happy coding! 🚀