4 ms·
Inconsistencies between types and rest of code _is_ bugs. I'm willing to bet my house that "TypeError: object doesn't support property or method" is the #1 bug
by Jestar342 6y ago
Inconsistencies between types and rest of code _is_ bugs. I'm willing to bet my house that "TypeError: object doesn't support property or method" is the #1 bug in JavaScript's sphere.
- timdaub 6y ago100% agreed. But my point is that I usually find these types of bugs really easy when unit testing.
- KronisLV 6y agoThe assumption here is that there are unit tests that cover the areas containing these sorts of bugs. It can prove to be false because many projects don't have sufficiently good code coverage. Of course, it's also possible that there simply are no unit tests at all. In those circumstances, TypeScript might just result in less bugs overall. While one could argue that unit testing should be commonplace, reality doesn't always live up to that standard.
- kungito 6y agoReal unit tests (not integration tests) are poor man's types. Usually when you have very small unit tests asserting inputs and outputs of methods you would simply assert those with types in more powerful languages
- hajile 6y agoHaskell has pure functions everywhere and a much stronger typing system than TS, but unit testing is still considered important. Someone else will be changing that code later. Knowing (for example) that the parameter is a string will be zero help in knowing what is being done with that string. It also won't help know what edge cases the code was handling. Also, types give a false sense of security. A new project was integrated into an old website (one with legacy dependencies, but doing millions in transactions every day, so unchangeable). It broke and they couldn't figure out why. They had the TS types for functions, but an old framework (prototype or moo iirc) overrode js built-ins with incompatible versions. Later they got bitten again when that code changed the object types. If they'd been writing js, they would have written dynamic checks from the start, but it's easy to forget that once you compile, it's just js.
- kungito 6y agoYou can always describe what has been done with the string by boxing it into an expressive type. Your other example is that when dealing with bad external code you need to do additional checks. That's a specific scenario and every language which exists has to deal with it in the same way. In more powerful languages you could infer which checks need to be done from the type and do the automatically for external code
- mlnj 6y agoTypes are just declarative unit tests for inputs and assignments.
- timdaub 6y agoLet's say you refactor code, then you might be inclined to change a type signature somewhere, which means you'll adjust your program until the compiler isn't complaining anymore. You'll work until the compiler is silent. The conpiler being silent, here is only an indication that your types and code match. Not that your assumptions about the program match with its outputs (e.g. the user interface). However, unit tests are usually written to assert assumptions. Or to "freeze" certain parts of the code so that these mismatches don't happen. In contrast: Types within the source code and depending on your way of thinking about types, many programmers will not see them as "declarative unit tests". In practice, this means that you sometimes get "surprised" by some unit tests that are failing after a refactor. That's good because it sheds light where you've made mistakes when changing your code. To some degree, of course, this is true for types. E.g. they will always help you to point out when two APIs mismatch. However, a test usually is contained within a unit with a clear description motivating its existence. It's so much harder to accidenitally changing a test for the worse than it is to change a typed function signature for the worse. Lastly, very often functions are crucially dependent on input values and not their types. So even if, in a dynamic language, you get b=0 input into div(a, b) return a/b and it's a valid type, you should test for values as in this case as you can't divide by 0. So in many cases, even with inputs it'd be necessary to unit test e.g. function signatures etc..
- acoard 6y ago>However, unit tests are usually written to assert assumptions. As you say, types are a more declarative (rather than procedural) way of asserting assumptions. >In practice, this means that you sometimes get "surprised" by some unit tests that are failing after a refactor. That's good because it sheds light where you've made mistakes when changing your code. >To some degree, of course, this is true for types. E.g. they will always help you to point out when two APIs mismatch. However, a test usually is contained within a unit with a clear description motivating its existence. It's so much harder to accidenitally changing a test for the worse than it is to change a typed function signature for the worse. I'd argue it's just as true for types as it is for unit tests, if not more so. You can get "surprised" by the compiler when refactoring methods in just the same way. I'd argue you get more information with types, because it hooks into the LSP and identifies everywhere in your code that now fails. In contrast, a unit test only tells you that unit test failed. It is still up to you to find the actual locations in the code. In this way, unit tests can be thought of as a parallel program. This isn't true of types, which are directly embedded into the program. Put concretely, if you removed a property on a type then the LSP and tsc would tell you every single place that property is missing. That's the crux of my argument. Types have better tooling, which helps with both the "delclarative unit test" part and equally importantly with refactoring. You can check for more things with types, like whether your `switch` statement exhaustively goes over every option or is missing any. Your tools also understand the types (like the LSP), which helps with refactoring. I'd also argue that types are usually easier to understand than tests. While good unit tests can provide good examples on how to use an API, types exhaustively tell you what a thing is and what it is capable of. >Lastly, very often functions are crucially dependent on input values and not their types. So even if, in a dynamic language, you get b=0 input into div(a, b) return a/b and it's a valid type, you should test for values as in this case as you can't divide by 0. That's a great point. There's absolutely still a place for tests. Types are not a like-for-like replacement, and each have their strengths, but there is significant overlap. If one wanted to exhaustively enumerate in options in a `switch` statement, and ensure division by 0 issues, then they would need both.