The Near Disaster
Three days before our Series A demo, our app crashed. The cause? A null reference that TypeScript should have caught—but didn't, because we weren't using strict mode.
// The bug that almost killed our funding
function getUser(id: string) {
return users.find(u =u.id === id);
// Returns User | undefined
// But we treated it as User
}
// Later in the code
const user = getUser(userId);
console.log(user.name); // 💥 Cannot read property 'name' of undefined
"That crash cost us 72 hours of sleep and nearly $2M in funding."
What is Strict Mode?
TypeScript's strict mode enables a set of type-checking flags that catch more errors:
| Flag | What It Does |
|---|---|
| strictNullChecks | null and undefined are distinct types |
| strictFunctionTypes | Stricter function parameter checking |
| strictBindCallApply | Correct types for bind, call, apply |
| strictPropertyInitialization | Class properties must be initialized |
| noImplicitAny | Must explicitly type 'any' usages |
| noImplicitThis | 'this' must have explicit type |
| alwaysStrict | Emit 'use strict' in all files |
// tsconfig.json - The nuclear option
{
"compilerOptions": {
"strict": true // Enables ALL strict flags
}
}
Our Migration Strategy
Week 1: Assessment- Enabled strict mode
- Counted errors: 847 😱
- Categorized by type and severity
- Fixed null reference issues (423 errors)
- Added explicit types where needed (312 errors)
- Updated third-party type definitions
- Fixed remaining edge cases
- Added custom type guards
- Documented patterns for team
// Before: Implicit any everywhere
function processData(data) {
return data.map(item =item.value);
}
// After: Explicit, safe types
function processData(data: DataItem[]): number[] {
return data.map(item =item.value);
}
// Type guard for runtime safety
function isDataItem(obj: unknown): obj is DataItem {
return typeof obj === 'object'
&& obj !== null
&& 'value' in obj;
}
What We Found
Error Breakdown:| Category | Count | Severity |
|---|---|---|
| Null/Undefined | 423 | Critical |
| Implicit Any | 312 | High |
| Property Access | 67 | Medium |
| Function Types | 45 | Low |
- 12 cases where null could reach database queries
- 8 cases where undefined could be sent to payment APIs
- 3 cases where type coercion hid data corruption
"Each of those 12 null cases was a potential production incident waiting to happen."
The Playbook
Phase 1: Enable strictNullChecks Only{
"compilerOptions": {
"strictNullChecks": true
}
}
{
"compilerOptions": {
"strictNullChecks": true,
"noImplicitAny": true
}
}
{
"compilerOptions": {
"strict": true
}
}
- Use
// @ts-expect-errorsparingly during migration
- Create type guards for external data
- Update tests to handle null cases
- Document common patterns for your team