How Cuttlefish Works

Cuttlefish reads your TypeScript and writes the C++ that runs on the board. Along the way it checks your code for common hardware mistakes — so you hear about problems in your editor, not after you upload.


In Short

When you run cuttlefish build, four things happen:

  1. Cuttlefish reads your TypeScript.
  2. It checks the code against the board you’re targeting — pin types, shared buses, memory use, and more.
  3. It writes out the matching C++.
  4. That C++ is compiled and uploaded the same way any Arduino sketch would be.

You write TypeScript. The board runs ordinary C++. Nothing unusual is loaded onto it.

Topics

TopicWhat you’ll learn
Type MappingHow your TypeScript types become C++ types
Zero-Cost AbstractionsWhich TypeScript features disappear completely — your code stays readable, the board runs lean code
Mistakes Cuttlefish CatchesThe checks that run on every build

How Your Code Becomes C++

A few quick examples of what the TypeScript-to-C++ translation looks like:

  • number becomes int, float, or a small whole-number type depending on how you use it
  • A string with ${...} placeholders becomes the kind of formatted text a board can print
  • Optional chaining (?.) and the ?? fallback become safe C++ checks that treat 0 and false as real values
  • An array becomes a plain C-style array — small and predictable, the way embedded code expects
  • A variable shared between a hardware interrupt and your main code is marked so the compiler doesn’t optimize it away

See Type Mapping for the full mapping table.

⚙️ Advanced details — the transpiler pipeline

Under the hood, every build runs four stages:

  1. Parse — your TypeScript is read into an in-memory tree (an AST) using the TypeScript compiler.
  2. Build — that tree is turned into Cuttlefish’s own internal representation (ProgramIR), with node types for expressions, statements, declarations, classes, functions, and enums.
  3. Validate — about 20 validators run against that representation: pin capabilities, peripheral conflicts, interrupt safety, heap use, memory budget, ownership rules, and more. See Conflict Detection.
  4. Emit — the validated representation is written out as C++ using the strategy for your target (Arduino, AVR-native, or generic).

Platform strategies

Framework-specific choices are delegated to a PlatformStrategy interface:

  • Arduino (@typecad/framework-arduino) — generates .ino sketches with setup()/loop() and compiles with arduino-cli.
  • AVR Native (@typecad/framework-avr) — generates direct register access instead of Arduino function calls.
  • Native (@typecad/framework-native) — generates standard C++ with main() for desktop builds.
  • Generic — fallback for unknown targets.

Strategies control type mapping, expression and statement rendering, interrupt attributes, build commands, and debug-code generation.

Source maps

Every build writes a .thcppmap.json file next to the C++. It maps each generated line back to your TypeScript, so when the C++ compiler reports an error, Cuttlefish can point you at the TypeScript that caused it. This powers the cuttlefish map-error command and the --diagnostics report. See Source-Mapped Diagnostics.

Incremental builds

Results are cached in .cuttlefish-cache.json, keyed off a fingerprint of the transpiler source files. If output ever looks stale after a change, clear the cache and rebuild:

rm src/.cuttlefish-cache.json
cuttlefish build