Good article. Good to see that critical thinkers still exist in this industry. You missed a few additional issues:
- Setup is more difficult because of more potential versioning and compatibility issues; when running on the backend, with TypeScript, you have to worry not only about the Node.js version you're using but also about the TypeScript compiler version (and developers in the wild can have a large number of permutations of these two) - Especially in open source ecosystems. TypeScript config options can become deprecated and this can break the project. It can be a challenge when you need to mix and match a range of libraries meant for different Node.js engine versions with different TypeScript versions (e.g. some dependencies require plain Node.js version x, others require TypeScript version y with Node.js version z, etc...)
- Difficult to debug in certain environments. Although source maps work in most environments, there are always environments where they don't work properly. I've worked on several TypeScript projects in the past where the source mapping did not work with `mocha` (which was used to run tests); it always incorrectly showed the error as happening on line 1. I think this issue was fixed in later versions of mocha. I've also encountered certain server and browser environments where source maps didn't work correctly or made it more difficult to debug. In production, source maps are too large so you generally don't send them to the client; this means that production error logs don't show accurate line numbers (I'm shocked that people actually put up with this; it's already a TS deal-breaker for me for most projects). Also, with TS, you can say goodbye to remote debugging for small or open source projects... You have to debug the ugly JavaScript transpiled code which is painful.
- It encourages developers to define overly complex interfaces which is bad for code quality. This is not an issue in theory, but it is an issue in practice 99% of the time. By allowing developers to easily keep track of what types are being passed to what functions, it encourages developers to define over-complicated function/method interfaces. Instead of passing simple raw types like numbers, strings, or raw JSON objects (without methods or mutable state), developers are encouraged to pass whole class instances with methods and mutable state; often, the reference to the same instance ends up spread out to multiple parts of the code and can be mutated in unexpected ways. TS often encourages developers to write spaghetti code which requires more effort to maintain and is more prone to bugs.
- It creates more reliance on advanced IDEs. Because TS encourages more spaghetti-ish code, every feature change requires more files to be changed. This means that you need an advanced IDE to keep track of all the parts of the code which need to be updated. If the code was written correctly, with better separation of concerns and looser coupling (thanks to simpler method interfaces), you wouldn't have to edit 10 different files every time you want to change a small feature. TypeScript proponents will often point to how IDEs can alert developers about where the types don't match up in the code as a reason why TS makes refactoring easier. However, note that the problem of "updating all the different parts of the code which are affected by a feature change" can be solved in two ways: 1. By making it easier to keep track of all those places. Or 2. By reducing the number of places impacted by a feature change (high cohesion). In the hands of enough junior and mid-level developers, TypeScript gives you #1 at the expense of #2 and IMO it hinders personal improvement.