I started programming in the 90s and have this version of history in my head:

  • 90s, java, c++, strongly typed, hit enter and wait
  • 00s, python! ruby! so much easier!
  • 10s, linters, typescript, flow, aaaaaand we’re back

My brain has for a while thought that separating types from compilation is clunky and bad, because:

  • dynamic language + buildstep = worst of both words – this is slow coming and going
  • translation layers (babel) have unknown runtime performance and require config maintenance
  • things that should be fast are slow – ‘running one test’ is a case I care about
  • jitters have to learn object shapes as they go, optimizability is hard to predict

But it’s been years since I used a strongly typed language full-time. I forgot what it’s like, and didn’t have the ‘JS with type ecosystem’ experience to compare it to.

Lately I’ve been using some rust and go and not feeling like ‘aahhh I’m back’. I’m less productive, sometimes dramatically so, and I don’t feel that much safer. Here’s what I think now:

  1. Get ready to bring your own types
  2. The clunk is real and we’ll add new tools

Get ready to bring your own types

I think that type-wise, we’re in the age of decoupling. That’s the difference from the previous waves of typed languages.

Javascript doesn’t ship with the ability to type check javascript programs (other than basic things like syntax checking and runtime exceptions for accessing fields of nulls). If you want this ability, you have to install and learn an extra thing.

That doesn’t have to be bad. It’s an opportunity – when the language doesn’t ship a type system, teams and communities have the freedom and incentive to develop their own. That means we can redefine what typesystems are and how specific they are to our needs.

Adults have always used external linters and verification. There used to be a burgeoning industry of C++ memory safety checkers, things like Valgrind that I never used myself and was legitimately afraid of. TLA is a thing, and corporations have in-house linters to enforce their rules.

The clunk is real and we’ll add new tools

All that said, external type checkers aren’t that extensible and are a pain to use. We need new kinds of tools to support plug-and-play verification.

I didn’t write this to brainstorm tools, but some thoughts about things that are on my wishlist:

  • AST diffing. Every language checker tool is going to need to know when something has changed and also what small piece changed so it doesn’t need to rebuild everything
  • Model-checking integration. Things like TLA and even coq have trouble creating runnable code. Function decorations that can render a codebase into a checkable model would be useful
  • Syntax stripping. It’s such a pain that I have to use webpack in order to use flow / typescript. JS (or future langs) should make it easier to plug in a custom parser while still using the rest of the runtime
  • Change linters: tools that can inspect VCS history to enforce rules like ‘don’t remove fields from protos’
  • Call graph and control flow analyzers. ‘Only call X under Y’, or ‘ensure that Y is called after X in a function’ are simple rules you some program analysis to solve. Future languages should ship with these.