Type Guard
Problem
TypeScript can’t infer specific types from conditional checks, forcing developers to use unsafe type assertions or ignore type errors. Union types remain too broad after validation, requiring casts that bypass type safety. Runtime checks exist but don’t help the type system, leaving code littered with type assertions and potential runtime crashes.
Solution
Write functions that narrow types based on runtime checks. This gives TypeScript information to provide accurate types in conditional branches.
Example
This demonstrates writing type guard functions that narrow types based on runtime checks, giving TypeScript accurate type information in conditional branches without unsafe type assertions.
// Type guard function with 'is' predicate
function isUser(obj: any): obj is User {
// Runtime validation that informs TypeScript's type system
return obj && typeof obj.name === 'string' && typeof obj.email === 'string';
}
if (isUser(data)) {
// TypeScript knows data is User here - no type assertion needed
console.log(data.email); // Safe access to email property
}
Benefits
- Eliminates unsafe type assertions by narrowing types correctly.
- Provides runtime validation that informs the type system.
- Makes code more maintainable with explicit type checking logic.
- Enables safer handling of union types and unknown data.
Tradeoffs
- Requires writing and maintaining type guard functions.
- Can be verbose for complex type checks.
- Runtime checks add small performance overhead.
- Type guards can become stale if types change but guards don’t update.