Introducing tcm

· Andrew Lisowski

tcm is a new way to store TypeScript. Instead of a folder of text files, your code lives in a codebase: a database of code. Each function, type and constant is stored on its own, under the hash of its code. Names are kept apart from the code, as labels that point at those hashes.

Writing code doesn't change. You write ordinary TypeScript in a scratch file, in the editor you already use. Once it typechecks, you add it to the codebase, which stores each definition in it. There's no new language: Node runs the stored code directly.

Keeping names apart from code makes everyday edits simpler. A rename moves a label, so no code is edited and nothing breaks. Changing a definition doesn't leave the code that uses it behind, either. tcm checks each place that uses it against the new version. The ones that still typecheck move to it, and the rest go on a todo list, with the reason.

Where it comes from

Most of the model is Unison's. Unison is a language built around storing code by its hash, and it showed what that makes possible: free renames, changes that carry through, scratch files that show values as you write, search by type, and history that knows a rename from an edit. tcm brings that model to TypeScript, and keeps the compiler, packages and editors people already use.

Sharing works as it does on Unison Share. Codebases are pushed and pulled by hash, and a contribution is a branch, reviewed as a diff of definitions rather than lines of text.

Your code as a graph

Each definition is stored with the hashes of what it uses, so a codebase is an exact graph of your code. A folder of files is a rough version of that graph: import one function from a file, and you depend on the whole file. With the exact graph, tcm knows what any change reaches, and much of what it does follows from that.

  • Tests run once. A test's result is kept with the hashes it reached. Only the tests a change reaches run again, and results travel with the code when you push and pull.
  • Apps ship only what they use. An app is bundled from what it reaches, so there's no dead code to remove.
  • Code runs where it belongs. Whether a definition runs on the server or in the browser is worked out from what it uses. Browser code calls a server function by importing it, with no routes or fetch calls. React server components work the same way, with no "use client" or "use server".
  • Deploys don't break open tabs. Calls from the browser to the server go by hash, and earlier builds stay served, so a tab keeps calling the code it loaded.

What goes away goes through each of these, and where it stops.

Try it

Install tcm and take the tutorial, or browse tcm's own code. tcm is developed in a codebase of its own, and its docs and this blog are pages in it.

All posts · RSS feed