6 ms·
I am empathetic to many of the reasons that folks enjoy using static types in JS, and there are many pros to using TS, but I don't find the arguments here to be
by spmurrayzzz 6y ago
I am empathetic to many of the reasons that folks enjoy using static types in JS, and there are many pros to using TS, but I don't find the arguments here to be persuasive at all. And as an engineering manager, I find some of these to be concerning.
Point #1 might be the strongest point, but it also assumes that your entire dependency graph is also type-safe, which is rare currently in the JS world (though that seems to be trending upwards).
Point #2 suggests that the class of bugs that were caught on the front-end could only otherwise be caught at compile-time by TS. In this example, its really just a question of whether you want syntax/semantics in your code to prevent these bugs or unit tests to assert correct application behavior. You could easily make the argument that the former is more expedient, I buy that. But it's not some exclusive domain of value in favor of TS. Arguably you could be using both approaches, but I find that increased surface area of unit tests (to cover the type error burden) is de minimis in the big picture.
Point #3 is similarly concerning. I hear this a lot from newer devs, that they can breathe a sigh of relief in thinking they can go make a ton of refactoring changes to their app and know that TS will prevent them from making egregious errors. But in fact, you can easily refactor an entire application and the output can be both type-safe _and_ have the behavior changed such that it no longer meets acceptance criteria.
On balance, if we had native type annotations in JS I'd use them, but I haven't really heard convincing value arguments (yet) to just go all-in with TS.
- ctvo 6y ago> But in fact, you can easily refactor an entire application and the output can be both type-safe _and_ have the behavior changed such that it no longer meets acceptance criteria. You're catching a set of errors (interface / types, method signature changes) that are difficult to catch in JavaScript without types when you're refactoring. Intended behavior is still validated with your set of unit tests. What you've avoided is introducing a new set of bugs that wouldn't be caught without strong typing. You swapped argument order in a private function somewhere, removed or changed a parameter, etc. that isn't caught by your unit tests and introduces subtle errors at runtime that TypeScript catches at transpilation time. That IS valuable on a larger codebase. You can claim unit tests should / would catch these errors, but rarely do unit tests catch all possibilities. They catch known and probable error conditions. The returns for time writing tests drop drastically as you try to push unit tests into the role of a statically typed compiler. TypeScript is popular because it's minimal friction while giving you a majority of those benefits.
- spmurrayzzz 6y ago>You're catching a set of errors (interface / types, method signature changes) that are difficult to catch in JavaScript without types when you're refactoring. Intended behavior is still validated with your set of unit tests. I want to challenge this a bit. Your unit test suite should also be able to catch all these things with the right assertions. There's nothing about JavaScript, in either syntax or semantics, that make these especially difficult to test for (notwithstanding some of the more famous footguns, but those are well known). To put it plainly... If I change the argument order of a function/method, the associated unit test absolutely should fail. If it doesn't, I'd argue the test wasn't written properly. To me, this just smells like an ergonomics preference. As in— do you prefer writing unit tests or type annotations? The latter comes with some transpiling and dependency overhead, so I've just opted for the former. (edited for grammar)
- striking 6y agoIt's not an either/or, the same way people who have access to professional editing staff probably still have a spell checker enabled in their word processor. If I have to hunt down each error caused by each refactor to the codebase, strictly based on a stack trace, I will not be happy. Being able to run a type checker that prints a list of probably wrong spots, and being able to check my unit tests after, makes me happy. And the overhead of introducing types is quite low, especially when using typesafe database tools like TypeORM and typesafe API tools like GraphQL. You basically get all your types for free. The question then becomes, why shouldn't we type check?
- spmurrayzzz 6y agoI don't think the assertion that types are "free" or "quite low" maps to my experience with JS tooling/dependency bloat. The transpiling burden is not minimal. I'm empathetic to the argument that because TS can find these errors easily, and descriptively, then it's a valuable tool. But your latter comment re: going through a stack trace, doesn't map to my experience with unit tests. If your test output is just a random exception in a stack trace, I would again argue that the test wasn't written correctly. This introduces an assertion burden for sure, ones you don't have to write if youre using TS, but I would posit that the exchange isn't as "free" as you claim.
- dragonwriter 6y ago> In this example, its really just a question of whether you want syntax/semantics in your code to prevent these bugs or unit tests to assert correct application behavior. You need to test behavior even when typing is correct, even if behavior tests will often also reveal type bugs. Still, type checking is more reliable at catching type errors, and does so earlier than even run-on-every-save unit tests. But it doesn't remove the need to test to assure correct behavior.
- spmurrayzzz 6y agoI totally agree with the sentiment that unit tests and type checking are not mutually exclusive and, in fact I'd go further to say, are mutually beneficial. I've said this elsewhere on this topic that if JS had native types, I'd probably use them. The assertion I am making here is a little more nuanced, insofar the premise of my argument is that as unit test coverage can and does offer a reliable (if not more verbose) way to assert type correctness of an application. Given that premise, I think its a deductively valid conclusion to make that the tradeoffs of introducing a significantly dense transpiling toolchain to the same application may not always be worth it.