Skip to content

How to migrate TypeScript to Hem

Read a readiness report, apply the safe subset, handle refusals, and decide where the tool stops and judgment starts.

Updated View as Markdown

hem migrate converts existing TypeScript that it can prove safe to convert, and refuses everything else. It is deliberately conservative: whole-file refusal is the safety contract, so a file either migrates completely or is left exactly as it was.

It is designed for migrating bounded slices of an application rather than converting an entire repository.

Look before you touch anything

hem migrate --check src/schedule.ts

This writes nothing. It prints a per-file, per-construct readiness report classifying every recognized construct into one of four categories:

Category Meaning What apply mode does
AUTO Provably behavior-preserving Rewrites it
SCAFFOLD Structure converts, semantics need a human sign-off Rewrites it with a needs-hands marker
FLAG A semantic judgment call Leaves it verbatim and reports it
NEEDS-HANDS An otherwise-automatic construct whose preconditions failed Leaves it verbatim with a marker

Read the FLAG entries first. These represent patterns that require manual review: || defaults, truthiness conditions, this outside a class, and any. The tool does not convert them automatically.

Pick a slice worth migrating

Choose a leaf module that has test coverage and active consumers, ensuring that existing behavior can be verified after migration.

Establish the baseline before changing any code. Run the package’s tests, build, and type check on the untouched source and record any existing failures to avoid confusing them with migration issues.

Apply the safe subset

hem migrate src/schedule.ts

Apply mode writes a .hem sibling for every file whose proposed migration both parses and type-checks as Hem. Any file that fails either gate is refused and left untouched. Every file it writes carries a banner marking it as migrated output for review.

Refusals are grouped by their first authored diagnostic, with counts. To see every diagnostic instead of the first blocker per file:

hem migrate --verbose src/schedule.ts

When a file refuses

You have three options, in rough order of preference.

Keep it in TypeScript. A narrow TypeScript sidecar holding a deliberate contract is a good outcome, not a failure. Hem is an application language with TypeScript at its boundaries.

Extract the implementation and keep the shell:

hem migrate --extract-implementation src/schedule.ts

This selects self-contained exported function implementations, writes them as .impl.hem, and rewrites only those bodies in the original file into exact delegating wrappers. Effectful top-level initialization and functions that capture state owned by the shell stay in TypeScript. Focused compilation, wrapper type checking, and an exact declaration comparison all run before anything is written.

Write a smaller Hem leaf by hand. Writing a concise, idiomatic module is often preferable to mechanically converting a file that failed automated checks.

Verify against the ABI, not the diff

The emitted TypeScript is the ABI, so review it. Compare the generated declarations against the originals and confirm nothing widened: exported types, mutability, optionality, overloads, generic contracts, and callable shape all have to survive.

Overload sets require manual work. The migrator does not turn an implementation into an overloaded function through an unchecked assertion. Rewrite the operation as a checked generic or separate functions first. A refused migration leaves the original TypeScript file untouched; it does not create a shim.

Then run the gates that prove behavior rather than shape: the module’s own tests, its consumers’ tests, the package build, and the type check you baselined earlier.

Feed a plan to a tool or an agent

hem migrate --check --json src/schedule.ts > plan.json

--json requires --check and emits the versioned machine-readable plan. Branch on each file’s accepted or refused outcome. Construct counts do not guarantee migration: a file with a high AUTO count can still be refused, and the refusal determines the outcome.

During the private preview, collaborators with authenticated Git access can install the Hem skill with npx skills add ozanvos/hem --skill hem; the repository link also requires access. It uses the CLI to assess and generate a candidate, then guides idiomatic rewriting, contract review, and behavior verification. Formal evidence ledgers and campaign tooling are optional references.

What this tool will not do

Semantic API redesign is a stated non-goal of both modes. hem migrate does not reinterpret truthiness checks, replace || with ??, or restructure interfaces; these changes require manual redesign.

For the full option list, see the CLI reference.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close