From TypeScript to Havran, file by file
You don't rewrite anything. Havran reads your existing types and emits code your toolchain already understands, so you can adopt it one file at a time.
Adopting a new language usually means a big-bang rewrite. Havran is designed to avoid that entirely: it reads 100% of your TypeScript and JavaScript, and the code it generates is just TypeScript. You can convert a single file and leave the rest untouched.
Step 1: rename and relax
Start by translating one module. Most TypeScript maps over almost mechanically:
data class Product(val id: Long, val name: String, val price: Double)
fun total(items: List<Product>): Double =
items.sumOf { it.price }interface Product {
readonly id: number
readonly name: string
readonly price: number
}
function total(items: ReadonlyArray<Product>): number {
let acc = 0
for (let i = 0; i < items.length; i++) acc += items[i].price
return acc
}Note the iteration lowers to an indexed for loop, not .reduce() — faster, and easier for engines to optimize.
Step 2: keep your imports
Havran imports your existing packages with no shims:
import { z } from "zod"
val schema = z.object(name = z.string())What stays the same
- Your bundler, test runner, and CI — unchanged.
- Your
node_modules— consumed directly, types and all. - Your output — ordinary
.js+.d.ts.
What gets better
| Concern | Before | After |
|---|---|---|
| Equality | === everywhere | == means value equality |
| Null handling | optional chaining | enforced at compile time |
| Bundle size | classes & helpers | tiered lowering, tree-shaken |
Migrate at your own pace. There's no point of no return.
When you're ready, convert the next file. That's the whole migration story.