In Kotlin/JS, what is the `external` keyword for, and why do `external` declarations have no body?
answer
- external = 'implemented in JS, type me only'
- No body allowed — signature only
- Keyword inherited inside external class/block
- definedExternally for default/optional params
- Compile-time typing, runtime must exist
basics
~10 sexternal tells the Kotlin compiler that something already exists in JavaScript and is implemented there. You only write its type signature, no body, so Kotlin can type-check your calls without re-implementing it.
solid answer
~40 s`external` marks a declaration whose implementation lives in JavaScript, not in Kotlin. It lets you write a typed Kotlin facade over an existing JS API (e.g. `external val window: Window` or `external fun alert(msg: String)`) so the compiler verifies your call sites against those signatures. The body is omitted because the real code is the JS runtime — providing one is an error. `external` is implicit inside an `external` block, class, or interface, so you don't repeat it on members. It only affects compilation: at runtime the JS function/object must actually exist, or you get a runtime error. Common in the `kotlinx.browser`/`org.w3c.dom` stdlib facades and in hand-written bindings for npm libraries. Members may use `definedExternally` (often written `= definedExternally`) to represent default-valued or optional parameters.
code
kotlin · 14 lines// Typed facade over existing JS — no bodies
external val console: Console
external interface Console {
fun log(message: Any?)
}
// Optional param whose default lives in JS
external fun setTimeout(handler: () -> Unit, timeout: Int = definedExternally): Int
fun main() {
console.log("hi") // type-checked
setTimeout({ console.log("later") }) // timeout omitted
}go deeper
Knows external declares JS-implemented APIs and that bodies are forbidden.
Explains keyword inheritance in external blocks, definedExternally, and that it's compile-time only.
Contrasts external (typed facade) with dynamic, knows stdlib facades (kotlinx.browser/w3c) are external, and the runtime-mismatch failure mode.
Weighs hand-written external bindings vs generated/dynamic access for maintainability, and how external facades shape an API surface for a team.
## What `external` means Kotlin/JS compiles Kotlin to JavaScript. Often you need to *call* JavaScript that already exists — the browser DOM, `console`, `window`, or an npm library. You don't want to reimplement those in Kotlin; you just want the compiler to **know their shapes** so it can type-check your calls. That is what `external` is for. An `external` declaration is a **typed promise**: "this name exists in JS at runtime with this signature; trust me." Because the implementation is in JS, you must **not** provide a Kotlin body — the compiler rejects one. ```kotlin external fun alert(message: String) external val window: Window external class Date { fun getTime(): Double } ``` ## Key rules - **No body.** `external fun f() { ... }` is a compile error. Just the signature. - **`external` is inherited downward.** Inside an `external class`/`interface`/`object` or an `external` block, every member is automatically external — you don't repeat the keyword. - **Compile-time only.** `external` changes nothing about the emitted JS for *your* code beyond name resolution; it just suppresses the need for a body and tells the type checker the shape. If the JS isn't actually there at runtime, you get a runtime `ReferenceError`/`TypeError` — Kotlin can't protect you. - **`definedExternally`** is a placeholder for a member/parameter whose value is supplied by JS. It's most often used to give optional parameters a default: `fun f(x: Int = definedExternally)`. You never assign a real value. ## Where you see it The Kotlin stdlib ships large hand-maintained `external` facades: `kotlinx.browser.window`, `kotlinx.browser.document`, and the `org.w3c.dom.*` types are all `external`. When you wrap an npm package you write your own `external` declarations (often paired with `@JsModule`/`@JsName`). ## external vs dynamic `external` is **typed** — you still get full Kotlin type checking against the signatures you declared. `dynamic` (a sibling concept) **disables** type checking entirely. Prefer `external` so the compiler keeps helping you; reach for `dynamic` only for genuinely free-form access.
- What happens if the JS function you declared `external` doesn't actually exist at runtime?Compilation still succeeds, but the call throws a runtime error (e.g. ReferenceError/TypeError) — `external` is a compile-time contract only.
- Do you need to repeat `external` on members of an `external class`?No. Membership in an external class/interface/object/block makes members implicitly external.
Like a header file or interface stub: you declare the shape so callers compile, while the actual code lives elsewhere.
saying these in an interview costs you the question
- Thinking `external` generates or imports the JS implementation for you
- Trying to give an `external` function a Kotlin body
- Believing `external` performs runtime checks that the symbol exists
- Confusing `external` (typed) with `dynamic` (no typing)
- Assigning a real value to a `definedExternally` member