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:
- Cuttlefish reads your TypeScript.
- It checks the code against the board you’re targeting — pin types, shared buses, memory use, and more.
- It writes out the matching C++.
- 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
| Topic | What you’ll learn |
|---|---|
| Type Mapping | How your TypeScript types become C++ types |
| Zero-Cost Abstractions | Which TypeScript features disappear completely — your code stays readable, the board runs lean code |
| Mistakes Cuttlefish Catches | The checks that run on every build |
How Your Code Becomes C++
A few quick examples of what the TypeScript-to-C++ translation looks like:
numberbecomesint,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 treat0andfalseas 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:
- Parse — your TypeScript is read into an in-memory tree (an AST) using the TypeScript compiler.
- Build — that tree is turned into Cuttlefish’s own internal representation (
ProgramIR), with node types for expressions, statements, declarations, classes, functions, and enums. - 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.
- 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.inosketches withsetup()/loop()and compiles witharduino-cli. - AVR Native (
@typecad/framework-avr) — generates direct register access instead of Arduino function calls. - Native (
@typecad/framework-native) — generates standard C++ withmain()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 buildOn This Page