I fought TypeScript for a long time. Every time someone suggested migrating a project, I had a list of objections ready. It slows you down. The type system is too complex. The error messages are unreadable. JavaScript is fine, just write better tests.
I was wrong. Not about all of it, some of those complaints are valid. But on the whole, TypeScript has made me significantly more productive, and I can't imagine going back.
The Learning Curve Is Real
Let me be honest. The first two weeks of writing TypeScript were miserable. I spent more time fighting the compiler than writing actual logic. Simple things that took one line in JavaScript suddenly needed type annotations, interfaces, generics, and union types. Code that worked perfectly at runtime was covered in red squiggly lines.
The error messages didn't help. TypeScript error messages are infamously verbose. "Type 'string | undefined' is not assignable to type 'string'" is the friendly version. Wait until you get a generic type mismatch that produces a 40-line error about deeply nested type incompatibility. My eyes glazed over more times than I'd like to admit.
But here's the thing: that pain was front-loaded. After about a month, I stopped fighting the compiler and started working with it. The types became documentation. The errors became guides. And the autocomplete became incredible.
Where TypeScript Actually Shines
Refactoring. This is the killer feature and nothing else comes close. In JavaScript, renaming a property or changing a function signature means grepping through the codebase and hoping you caught everything. In TypeScript, you rename it, and the compiler instantly shows you every place that needs to change. I refactored an entire API response shape last month across 30+ files in about 15 minutes. In JavaScript, that would have been an afternoon of careful searching and nervous testing.
Autocomplete that actually works. VS Code with TypeScript is a different experience than VS Code with JavaScript. The autocomplete knows exactly what properties are available, what types a function expects, what it returns. I almost never need to look up API documentation for libraries that ship type definitions. The editor tells me everything.
Catching bugs at compile time. I've lost count of how many times TypeScript has caught a bug that would have been a runtime error. Accessing a property that might be undefined. Passing arguments in the wrong order. Using a string where a number was expected. These are the dumb mistakes that slip through tests because you didn't write a test for that specific edge case.
Where It Still Annoys Me
I'm not going to pretend TypeScript is perfect. Typing third-party libraries that don't ship their own types is painful. The DefinitelyTyped community types are sometimes outdated or incorrect. And there are moments where you know your code is correct but TypeScript can't prove it, so you end up with a type assertion or an as any that feels like a defeat.
The build step adds complexity. One more thing to configure, one more thing that can break. For tiny scripts and prototypes, I still reach for plain JavaScript because the overhead isn't worth it.
And the type system rabbit hole is real. You can spend hours crafting the perfect generic type that handles every edge case. At some point you have to ask yourself: am I being productive, or am I playing type system sudoku? I've caught myself doing the latter more than once.
My Pragmatic Approach
I don't use TypeScript in strict mode for everything. For new projects with any real complexity, absolutely. But for quick prototypes or small scripts, plain JS is fine. The goal is productivity, not type system purity.
I keep my types simple. Interfaces over complex generics wherever possible. If a type definition is harder to understand than the code it describes, something has gone wrong. I'd rather have a slightly less precise type that's readable than a perfectly precise type that nobody can understand.
I use unknown instead of any when I genuinely don't know the type. It forces me to narrow the type before using it, which is exactly the kind of safety net I want.
The Verdict
TypeScript's value is proportional to the size and lifespan of your project. For a weekend hack, it's overhead. For anything you'll maintain for more than a few months, or anything with more than one contributor, it's practically essential.
The pain is temporary. The productivity gains are permanent. If you've been on the fence, just try it on your next real project. Not a todo app, an actual project with API calls, state management, and data transformations. That's where you'll feel the difference.