Smallest possible output
Tiered lowering avoids JS classes until they're truly needed. Data classes erase to plain objects; enums to string unions. You ship bytes, not boilerplate.
The clean, type-safe language that compiles to the smallest, fastest JavaScript — and reads 100% of your TypeScript.
No === weirdness. No null surprises. No runtime bloat.
// Better browser APIs
// Start listening
el.onKeyDown {
// the auto-declared `it` is the first lambda parameter
log.info(it.key)
}.dispose() // JS removeEventListener
// Unified API for timers
timeout(500) {
log.info("delayed")
}.dispose() // JS clearTimeout
// Erase-first at runtime
data class MyReq(
val id: Long?,
// default-values
val name: String = "default"
)
class MyClass(val name: String) {
// null is undefined in JS
var y: Int? = null
// Uses Temporal, but also fully compatible with JS Date
var lastUpdate: Date
// no more `this.` bloat
fun updateY(x: Int) {
y = x * 2 // this.y is not required
lastUpdate = Date.now() // this.lastUpdate
}
// function overloading
fun doSomething(a: String) {}
fun doSomething(b: ZonedDateTime) {}
// ...see more features in the showcase
}
// Named args + defaults — name them, any order, skip the rest
fun send(to: String, subject: String = "(no subject)", retries: Int = 3) {}
// skip defaults with named args
send("[email protected]", retries = 5)
// Auto parse & stringify
val obj = MyClass("Joe")
// auto - stringify
Storage.local.set("key", obj)
// Auto JSON.parse with nullability
val parsed = Storage.local.get<MyClass?>("key")
// Direct usage
val y = parsed?.y
// Cookies live under the same typed API
Storage.cookie.set("token", session, maxAge = 3600)
val token = Storage.cookie.get<String?>("token")
// Extension functions + one-line functions
fun String.slugify() = trim().doSlugify()
// call it with dot notation
" Hello There ".slugify()
// Angular (React, Vue...) reactivity
// operator overloading
val reactiveVal by signal("hello")
// reactiveVal.set(reactiveVal() + 2)
reactiveVal += 2
// reactiveVal() to get
log.info("The value is $reactiveVal")
// Operators + real decimals — just write a + b
val a = Money(BigDecimal("9.99"))
val b = Money(BigDecimal("0.01"))
val c = a + b
// return type is auto-wrapped in Promise<UserResp>
suspend fun load(): UserResp {
// every Promise call is auto-awaited inside suspend
val resp = callUser<UserResp?>()
resp?.let {
// `it` is the non-null UserResp
doSomething(it)
}
}
// scope functions (apply, with, let, ...)
callSomething(MyClass("name").apply { y = 5 })
// One `is` for every shape — no typeof / instanceof / in juggling
val typeName = when (obj) {
is String -> "string"
is Int -> "number"
is Boolean -> "boolean"
is List<*> -> "array"
is MyReq -> "data class"
is () -> Unit -> "function"
is ZonedDateTime -> "zoned date-time"
null -> "absent"
else -> "unknown"
}
// ...see more features in the showcase section// Better browser APIs
// the trailing lambda becomes a named handler
const onKeyDown = (it: KeyboardEvent) => {
log.info(it.key)
}
el.addEventListener("keydown", onKeyDown)
el.removeEventListener("keydown", onKeyDown) // l.dispose()
// Unified API for timers
const t = setTimeout(() => {
log.info("delayed")
}, 500)
clearTimeout(t) // t.dispose()
// Erase-first at runtime
interface MyReq {
readonly id: number | undefined
// cannot add default value (done differently)
readonly name: string
}
class MyClass {
constructor(readonly name: string) {}
y: number | undefined = undefined // null is undefined in JS
// Uses Temporal, but also fully compatible with JS Date
lastUpdate!: Temporal.Instant
// `this.` keyword bloat
updateY(x: number): void {
this.y = x * 2
this.lastUpdate = Temporal.Now.instant()
}
// two overloads → one union implementation
doSomething(arg: string | Temporal.ZonedDateTime): void {}
// Havran resolves the branch by type
// ...see more features in the showcase
}
// Positional only — pass undefined to skip, and you can't reorder
function send(to: string, subject = "(no subject)", retries = 3) {}
send("[email protected]", undefined, 5)
// Manual parsing & stringifying
const obj = new MyClass("Joe")
// you stringify by hand
localStorage.setItem("key", JSON.stringify(obj))
// and JSON.parse, with a manual null-check
const raw = localStorage.getItem("key")
const parsed: MyClass = JSON.parse(raw)
const y = parsed?.y
// document.cookie: hand-encode, then parse the whole jar
document.cookie = `token=${encodeURIComponent(session)}; max-age=3600; path=/`
const token = decodeURIComponent(document.cookie.split("; ").find((c) => c.startsWith("token="))?.slice(6) ?? "")
// Extension functions → standalone helpers
const String_slugify = (self: string) => String_doSlugify(self.trim())
// the receiver becomes the first argument
String_slugify(" Hello There ")
// Angular (React, Vue, ...) reactivity
// `by` unwraps the signal accessor
const reactiveVal = signal("hello")
// `+=` lowers to a read + a set
reactiveVal.set(reactiveVal() + 2)
// and reactiveVal() reads the value
log.info(`The value is ${reactiveVal()}`)
// no operator overloading, no decimal type — all method calls
const a = new Money(new BigDecimal("9.99"))
const b = new Money(new BigDecimal("0.01"))
const c = new Money(a.x.plus(b.x)) // the "a + b" you don't get
// suspend → async returning Promise<UserResp>
async function load(): Promise<UserResp> {
// suspend inserts the await on every Promise call
const resp = await callUser<UserResp | undefined>()
if (resp != null) {
// smart-cast: it is the non-null UserResp
doSomething(resp)
}
}
// requires a variable
const obj = new MyClass("name")
obj.y = 5
callSomething(obj)
// JS needs a different check per shape — and order matters
let typeName: string
// null first: `in` and property access throw on null
if (obj == null) typeName = "absent"
else if (typeof obj === "string") typeName = "string"
else if (typeof obj === "number") typeName = "number"
else if (typeof obj === "boolean") typeName = "boolean"
else if (Array.isArray(obj)) typeName = "array"
else if ("id" in obj && "name" in obj) typeName = "data class"
else if (typeof obj === "function") typeName = "function"
else if (obj instanceof Temporal.ZonedDateTime) typeName = "zoned date-time"
else typeName = "unknown"
// ...see more features in the showcase sectionHavran is designed around one promise: the clarity of a modern, expressive language with the smallest, most interoperable output a transpiler can produce.
Tiered lowering avoids JS classes until they're truly needed. Data classes erase to plain objects; enums to string unions. You ship bytes, not boilerplate.
Havran reads every type from your existing TypeScript and JavaScript — libraries included — and emits code any TS toolchain consumes natively.
One nullable model: `String?` lowers to `string | undefined`, `?.` and `?:` just work. The compiler stops null bugs before they ship.
Comparing two different types is a compile error. `==` means value equality. No truthy coercion, no `===` to remember, no `NaN` surprises.
`when` expressions, data & sealed classes, extension and scope functions, ranges, and string interpolation — the expressive syntax you already love, on the web.
Iteration emits indexed `for` loops, numbers stay `number`, and the runtime is side-effect-free and fully tree-shakeable. Fast by default.
Every snippet, table, and note below comes straight from the language brief — the real lowering, not a marketing mock-up. Scan the fold, then expand for the full tour.
Havran is a web programming language that transpiles into clean, modern and very restrictive TypeScript and JavaScript without any JS weirdness with 100% mutual interoperability with your existing TS/JS codebase. You write expressive, type-safe code; Havran emits the smallest, fastest output a compiler can produce.
Havran is a strict superset of intent, not syntax: it reads 100% of your existing TypeScript and JavaScript — types, libraries, everything — and the code it generates is consumed natively by any TypeScript toolchain. You can adopt it file-by-file.
TypeScript is still JavaScript but with types. Havran is a new web language that removes the sharp edges: no `===`, no truthy coercion, comparing two different types is a compile error, and nullability has a single clear model. You also get data classes, sealed types, `when` expressions, and scope functions — with zero runtime cost where possible. See the features list for comparison.
No. Havran aspires to produce the lowest possible JS bundle with the maximal JS performance. Tiered lowering avoids emitting JavaScript classes until a feature truly needs them — data classes become plain objects, enums become string unions, iteration becomes indexed `for` loops, and the runtime is side-effect-free and tree-shakeable. See the comparison list to convince yourself.
Yes. Because the output is ordinary TypeScript, Havran works with React, Angular, Vue, Svelte, Solid, Astro, Next.js, Nest.js and plain Node — no special bindings or runtime required.
Havran is in active development in the late alpha phase. For now, we offer only live playground where you can experiment with it. For now, we offer only a live playground where you can experiment with the language. Since creating a programming language is not an easy task even in the age of AI, we kindly ask you for donations to let us know if we should continue or not. The language and compiler are evolving quickly — join the community below to follow along, try it out, and shape where it goes next.
It's not Kotlin/JS at all. We took only the Kotlin-like syntax and merged it with Kotlin, TypeScript, and our features that web development needs. Havran is built with native support for JS/TS and the existing web frameworks ecosystem - nothing related to Java or Gradle.
No. Developers can gradually switch codebases to Havran, without needing to rewrite everything at once. Havran is designed to be a seamless addition to your existing TypeScript codebase, allowing you to adopt it incrementally and enjoy its benefits without a complete overhaul.
We offer 100% interoperability with existing TypeScript/JavaScript codebases. Havran is a merge of TypeScript, Kotlin and our features together. We offer unique features such as anonymous types, dynamic classes, advanced generics, built-in reflection, environment-strictness utils (browser/server), custom managed suspended context, more types, type-based and simplified Browser API and so much more. See the feature list for deep exploration.
For web development, you should still know JavaScript to understand the core principles. We'll continuously improve our docs to help you understand the Havran syntax and features, but having a background in Kotlin can be beneficial for grasping the language's design and features more quickly. However, it's not a strict requirement to get started with Havran.
We are independent software engineers with nearly two decades of software development in various technologies such as JavaScript, TypeScript, Java, C, and Kotlin. We have all delivered numerous web/server projects, and we are tired of JavaScript weirdeness with the ambitious to stop writing in JavaScript whatsoever! But for this, we kindly ask you to make a donation for us to keep the project up and running. Big thank you!
We are still making fundamental and optimization changes, so we will open-source it as soon as we finish the fundamentals. To make it happen, please let us know you're interested by donating to us, sign-up to a newsletter, and sharing with your software-engineer colleagues. We thank you! For now, you can see the language progress in our live playground. We are open to your feedback!
We don't recommend using any escape to raw JavaScript / TypeScript. However, for inevitable use cases, it is still possible, but you should prefer the strict approach in terms of types, nullability, mutability, and other principles. However, Havran offers 100% mutual interoperability with TypeScript/JavaScript from day one.
Havran is built in the open by a tiny team — no VC, no paywalls. Your sponsorship pays for the development, infrastructure, and time that keep it fast, free, and yours. If Havran saves you even one afternoon of debugging, consider giving a little back.
Cancel anytime. Monthly support is what keeps Havran moving.
Buy the team a coffee every month and keep the lights on.
Keep a feature in active development, month after month.
Power a steady stream of releases and faster fixes.
Want the full breakdown and company tiers? See all sponsorship options →
Havran is built in the open. Subscribe for updates, jump into the community, share it with a friend, and tell us what you'd build with it.
Occasional updates on releases, the roadmap, and deep dives. No spam, ever.
🔒 We store your email only to send you Havran updates. We never share it, and you can unsubscribe or ask us to delete it any time at [email protected] .
Havran is free and built in the open. A monthly or one-time sponsorship keeps it fast, independent, and yours.
Become a sponsor →Know someone tired of JavaScript footguns? Send them this page.
Got a feature request, a rough edge, or a wild idea? We genuinely want to hear it.
Give feedback →