I came to TypeScript from Python. Strong typing felt natural to me — but JavaScript's type system felt like fighting quicksand.
For the first six months, I added // @ts-ignore comments like they were going out of style.
The Turning Point
A bug slipped into production. A function expected { userId: string } but received { user_id: string } — snake_case vs. camelCase mismatch between our Python API and React frontend.
TypeScript would have caught it at compile time. We caught it at 2am.
After that, I enabled strict: true and deleted every // @ts-ignore.
What Strict Mode Actually Forces You to Fix
// Before strict mode — this is technically valid JS
function getUser(id) {
return users.find(u => u.id === id);
}
// After strict mode — you're forced to think
function getUser(id: string): User | undefined {
return users.find(u => u.id === id);
}
The | undefined return type is the important part. Every caller now has to acknowledge the possibility that the user doesn't exist. You've encoded a business rule into the type system.
Discriminated Unions Changed How I Design APIs
type ApiResult<T> =
| { success: true; data: T }
| { success: false; error: string };
This is essentially Python's Result pattern. The UI can't accidentally try to render .data on an error response — TypeScript prevents it at compile time.
The Taxes Worth Paying
Yes, TypeScript adds overhead:
- Generics syntax is verbose
- Type narrowing is sometimes counterintuitive
- External library types are sometimes wrong or missing
But the benefits compound over time. A codebase six months old with strict TypeScript is vastly more navigable than the equivalent JavaScript, especially for new contributors.
My Setup That Makes It Bearable
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
}
noUncheckedIndexedAccess is the hidden gem. It forces you to handle undefined when accessing array indices or object properties by string key. It's annoying at first and then you realize every bug it flags is a real one.
The fight isn't with TypeScript. The fight is with your own assumptions about what your data looks like. TypeScript just makes the fight happen at compile time instead of production.