The biggest advantage of static typing, in my opinion, is that simple refactorings like "rename" or "move method from one class to another class" or "change what method does and then modify all places that call it" are done routinely and very often. Where renaming something in javascript is careful change you do when fully fresh, rested and focused, it is one quick right click when you are lazy and too tired to do real work in statically typed code. Means difference between rarely done and often done.
The other big advantage is when I am learning somebody elses code, all info about who calls who and how is right there.
You are posting about static typing advantages. Please note that the use case you mention would not need too much refactoring in languages like Common Lisp (dynamic language) or Julia (dynamic), because of multimethods and because of the way methods are called. Methods -and method signatures- aren't constrained inside class definitions, they are separated.
The use case I had in mind was that I delete method from one class and create it in another. Then I go through all red in workspace and modify what is needed - sometimes just call the other place, other times remove the call entirely, yet other times conclude that method needs to go back because that two special places that don't have access to the other class instance.
Similar with changing signatures - I add/remove parameters and then go through all reds and decide how to fix them case by case.
Note that this is very doable in a dynamic language environment like Smalltalk or Lisp: you're constantly interacting with the running system, and so can easily rename a function or method, update all source locations, update the class of live instances (e.g. UPDATE-INSTANCE-FOR-REDEFINED-CLASS[0]) and even change the class of live instance (e.g. UPDATE-INSTANCE-FOR-DIFFERENT-CLASS[1]).
> The other big advantage is when I am learning somebody elses code, all info about who calls who and how is right there.
Again, a live environment is capable of tracking all callers & callees. It's pretty awesome, and honestly the only way I'd prefer to develop.
[0] http://clhs.lisp.se/Body/f_upda_1.htm [1] http://clhs.lisp.se/Body/f_update.htm
They catch 15% of the bugs in Javascript projects on GitHub.
Why do people insist on restricting their static code analysis into heuristic rules when there are so many perfectly good complete rules to be checked with a type system?
Using JS projects in GitHub is a really misleading sample. GitHub is used by all kinds of purposes, for example most coding bootcamps and institutes here tell the students to upload their code to GitHub. You have, for each institution, about 400 Javascript GitHub repositories each year, full of code written by true beginners.
GitHub is simply a big container of code for literally everybody, so judging kind of bugs produced by looking at what it's inside the Github repositories is akin to judging how many gramatical errors book authors make by taking a look at all the MS word documents stored in Dropbox.
Because it's trivially easy to show cases where a static type system rejects useful and correct code.
Yes, static type systems do "eliminate whole classes of errors", but they also eliminate whole classes of correct code! Is the freedom from (some, not all!) runtime type errors really worth the loss of expressive power?
Well, the answer is of course "it depends" on specific circumstances: sometimes it's worth it, sometimes it's not and most of the time the very choice (ie. should I write in statically or dynamically typed language in this case?) is not important at all because tons of other choices have much more of an impact on whether the project succeeds or not.
I have found the types to be useful in a large (for JS) codebase with more than a handful of developers. The types help catch bugs, especially when refactoring, even with a decent test suite and linting. Plus, using React and GraphQL, we get static types all the way from data coming from the server through to the UI.
We need to talk about costs though. If a type system lets you write 15% fewer unit tests, it's worth it for that alone.
> There are static code analyzers (eg. tern.js) that provide hinting without requiring a transpiler
A static analyser is already a type system. You need a way to hint to the analyser, at which point you've got a notation for types in your language as well (maybe a limited one). You want the analyser to run on every build anyway, so a transpiler is no more effort; if you make the analyser optional developers will skip it, and will introduce bugs as a result, so it's better to simply not have the option.
> With Flow and Typescript you have transpiler overhead, but when it comes to places you might need them the most - checking and sanitizing data interchange that's so common in modular, service-oriented architecture - they fall flat.
You need to use types on the interfaces too. Use something like thrift.
I think it's a stretch to say a static analyzer is "a type system". It doesn't obviate runtime type checking and implicit type conversion. But yes, absolutely you should run it on every build.
Of course they are: whatever your acceptable defect rate, both types and tests are ways to spend effort towards achieving it. The more defects you can eliminate via one, the less you need to catch via the other.
> I think it's still worthwhile because, like I said, you can have code where all the type constraints are satisfied but become violated by ingested data.
Most languages won't allow that to propagate through the system, and if you get an error at the boundary then it's obvious what the error is. (Admittedly a compile-to-Javascript language is the example here and may be the exception).
Fundamentally you decide how likely certain classes of errors are and look at the cost/benefit of the various measures available to you. In my experience types can, when you commit to them and work with them, reduce the defect rate very close to zero, and more cheaply than any other measure; I only feel the need to supplement them with tests very occasionally, and only for the very core parts of the code where defects would be most expensive.
> I think it's a stretch to say a static analyzer is "a type system". It doesn't obviate runtime type checking and implicit type conversion.
In terms of the mechanics of what it does, it's a type system. I don't see any value in having two type systems for the same code - type systems work better if they're part of the language so that all of the tools understand the same types the same way - so I prefer to use a language where the language type system covers all the use cases that a static analyzer might be helpful for.
This can't be repeated often enough.