My very first programming language was ActionScript 2 (which was also based on the ECMAScript standard without types; just like JavaScript) and I clearly remember that I was really enjoying ActionScript 3 when it came out in 2006 (that's when ActionScript became a statically typed language; very similar to TypeScript).
Then a few years later, I got back to JavaScript/Node.js (dynamically typed) and I noticed that I could create libraries and applications a LOT faster without types and that writing libraries was also a lot more enjoyable.
My view now is that statically typed languages are like training wheels on a bike. Statically typed languages force you to think about software in a structured way and in doing so, they teach you some fundamental concepts about abstraction. That said, if you're experienced enough to know how to structure and test your code correctly, then, much like training wheels on a bike; they only slow you down.
I've been using TypeScript for the past couple of years at work so I can confidently say that it creates more obstacles than it solves for me.
Pros:
- Easier to keep track of all the places in the code which are affected by a refactoring (especially if the code is messy, poorly tested and components have poor separation of concerns; it gives useful warnings).
- Easier to find class and method definitions in the code.
- Easier to rename classes, functions and properties.
- Much less likely to leave forgotten unused variables or functions in your code.
Cons:
- Slow debug cycle due to increased compile time. If using TDD, it takes longer to run tests.
- Sporadic source mapping issues/bugs which makes it hard to fix things (I had an issue with Mocha tests last week where it was always telling me that the issue was on line 1). I've worked with TypeScript on multiple projects across different companies but every single time, there has been source mapping or configuration issues of some kind.
- Type definitions from DefinitelyTyped are often out of date or have missing properties/methods. It means that I can't use the latest version of a library.
- Third party libraries are difficult to integrate into my project's code due to conflicts in type names or structural philosophy (e.g. they leverage types to impose constraints which are inconsistent with my project requirements).
- Doesn't guarantee type safety at runtime; e.g. If parsing an object from JSON at runtime; you still need to do type validation explicitly. The 'unknown' type helps but it's not clear that this whole flow of validating unknown data and then casting to a known type adds any value over regular schema validation done in plain JavaScript.
- Whenever I write any class/method, I have to spend a considerable amount of time and energy thinking about how to impose constraints on the user of the library/module instead of assuming that the user is an intelligent person who knows what they're doing.
- Compiler warnings make it hard to test code quickly using console.log(...); I'm more reliant on clunky debuggers which slows me down a lot and breaks my train of thought.
- The 'rename symbol' feature supported by some IDEs is nice, but if a class or property is mentioned inside a string (e.g. in a test case definition) then it will not rename that; so I still have to do text search after doing a symbol rename and manually clean up.
- It takes a lot of time think of good names for interfaces and they are renamed often as project requirements evolve. It's not always clear whether similar concepts should be different types or merged into one. It often comes down to what constraints you want to impose on users; This adds a whole layer of unnecessary mental work which could have been better spent on thinking about logic only.
- Setting up TypeScript is a pain.
- Adds a lot of dependencies and complexity.
- Makes the bundling step a necessity. This goes against innovations like HTTP 2 which could potentially remove the necessity of the bundling step by allowing us to preemptively push scripts to the client.