The more code I write in dynamically typed languages, the more I believe that static typing is a must.
Generally speaking, I noticed that I'm shifting more and more away from "stop annoying me and let me do what I want, I know better!" part of the PL/API design spectrum, and towards "better safe than sorry". Static typing, runtime checks, data schemas, design by contract, fail fast etc. Yes, it's overhead, but it's predictable overhead - unlike, say, trying to debug an incomprehensible error the night before release.
One downside to static typing is the overhead required when writing/running programs.
For example, let's say you have a class `Dog` that you want to rename to be more generic so you now call it `Animal`.
In Python you can test out snippets of code with `Animal` without necessarily having to worry about other pieces of code still referring to `Dog`. You would just need to make sure that the code you want to run happens before later pieces that might use `Dog`.
In C/C++ you'd have to change all the references to `Dog` in your codebase, comment them all out, or add a new target to your project to build a subset of files which do not include an invalid reference to `Dog`.
This extra friction can make rapid prototyping a little harder in static typed languages.
IDK if any language supports this today, but ideally I'd like to be able to run a language without compile-time type checking for prototyping and be able to progressively turn on type checking as I'm nearing the final stages of iteration on a project/feature.
In a statically typed language with decent tooling, you'd just tell it to rename all references of Dog to Animal everywhere, and it will do that reliably. That's one other advantage of static typing - fearless name refactoring, which makes in that much easier to clearly express intent of the code even as it changes.
i've only used the native one but i've found it fails on some edge cases where it can't detect variables outside a certain scope. still trying to reproduce it but has made me double check all references since then
JavaScript and TypeScript. TypeScript is a superset of JS so you can write vanilla JS in TypeScript code.
Also, having worked extensively with Java, changing types is not as nearly as cumbersome as you describe. In an IDE, you can replace all usages of a type in a project or subdirectory with 3 clicks. Large, statically typed codebases are almost always navigated in an IDE.
Generally speaking, I noticed that I'm shifting more and more away from "stop annoying me and let me do what I want, I know better!" part of the PL/API design spectrum, and towards "better safe than sorry". Static typing, runtime checks, data schemas, design by contract, fail fast etc. Yes, it's overhead, but it's predictable overhead - unlike, say, trying to debug an incomprehensible error the night before release.