Explain `definedExternally` and the `js("...")` function. How do they relate to `external` and `dynamic`, and what are the pitfalls?
answer
- definedExternally = JS supplies the default; external-only
- Never assign a real value to definedExternally
- js("...") inlines literal JS, returns dynamic
- js() argument must be a compile-time constant
- Pin js() result back with unsafeCast / external
basics
~20 sdefinedExternally is a placeholder used in external declarations to say 'the default/value comes from JS, not Kotlin'. js("...") embeds a literal JavaScript expression and returns it as dynamic. Both are interop tools whose mistakes only show up at runtime.
solid answer
~50 s`definedExternally` is a special stub value usable **only inside `external` declarations**. Its main use is giving a default to an optional parameter — `fun f(x: Int = definedExternally)` — telling Kotlin the actual default is supplied by JS, so callers may omit the argument. You never assign a real value to it. `js("...")` takes a **compile-time constant string** of JavaScript, inlines it verbatim into the output, and types the result as `dynamic`; it's the rawest interop escape hatch (`val n: dynamic = js("Math.random()")`). Pitfalls: the `js()` argument must be a literal — you can't build it dynamically, so there's no string-interpolation injection point, but also no Kotlin variable can be referenced inside it directly. Both bypass type checking, so typos and bad JS surface as runtime errors. Idiomatically, wrap `js("...")` results in typed `external`/`unsafeCast` boundaries rather than spreading the `dynamic` it returns.
code
kotlin · 11 lines// definedExternally: JS-supplied default in a typed facade
external fun fetch(url: String, opts: dynamic = definedExternally): dynamic
// js("..."): literal JS, must be a constant string, returns dynamic
val now: dynamic = js("Date.now()")
// pin the dynamic result back to a real type
val millis: Double = now.unsafeCast<Double>()
// WRONG: can't splice a Kotlin variable into the literal
// val x = js("foo(" + kotlinVar + ")") // compile error: not a constantgo deeper
Recognizes definedExternally as a placeholder and js("...") as inline JS.
Explains definedExternally for optional params and that js() returns dynamic.
Knows the constant-string constraint, why variables can't be spliced, and narrows js() results with unsafeCast.
Governs where raw js(...) is acceptable, prefers typed facades, and minimizes the untyped/literal-JS surface across a codebase.
## `definedExternally` Inside an `external` declaration you sometimes need to *stand in* for a value or default that JavaScript actually provides. `definedExternally` is that placeholder. The compiler treats it as 'the real thing lives in JS — don't expect a Kotlin value here.' Most common use: **optional parameters**. ```kotlin external fun open(url: String, target: String = definedExternally): Window? open("/page") // target omitted — JS default applies open("/page", "_blank") // target supplied ``` Rules: - Valid **only** in `external` contexts (external functions/classes/interfaces). - You never assign a concrete value to it; it's a marker, not data. - It lets you express JS-style optional args while keeping a typed signature. ## `js("...")` `js` is a stdlib function that **inlines a literal JavaScript expression** into the compiled output and returns it typed as `dynamic`. ```kotlin val random: dynamic = js("Math.random()") val obj: dynamic = js("({ a: 1, b: 2 })") ``` Key constraints: - The argument **must be a compile-time string constant** — you cannot pass a computed/variable string. This is enforced by the compiler. - The result is `dynamic`, so it carries no type information. - The JS is emitted essentially verbatim; it runs in the surrounding scope of the generated code. ## How they relate to `external`/`dynamic` - `definedExternally` is a detail **of** `external` declarations — it makes typed facades express JS optionality. - `js("...")` is the rawest **producer of `dynamic`** — it's how you drop to literal JS when no facade exists. Its output flows into the same untyped `dynamic` world. So the spectrum is: typed `external` facade (safest) → `dynamic`/`js(...)` raw access (least safe) → narrow back via `unsafeCast<T>()`. ## Pitfalls - **Runtime-only correctness.** Bad JS in `js("...")`, or a wrong `external` default, compiles fine and fails at runtime. - **No Kotlin variables inside `js(...)`.** Because the argument is a literal constant, you can't splice Kotlin values into the JS string; pass them as function parameters of an `external`/typed wrapper instead. - **`dynamic` spread.** `js(...)` returns `dynamic`; if you don't pin it back, untyped values leak through the codebase. - **Misusing `definedExternally`.** Treating it as a real default value, or using it outside `external`, is an error. ## Takeaway Use `definedExternally` to model JS optional params in typed facades; use `js("...")` sparingly as a last-resort literal-JS hatch, then immediately wrap or `unsafeCast` its `dynamic` result into typed code.
- Why must the argument to `js("...")` be a compile-time constant?The compiler inlines the string verbatim into the generated JS at compile time, so it cannot be a runtime-computed value; this is enforced and also avoids dynamic code-injection patterns.
- Can you reference a Kotlin variable directly inside a `js("...")` string?No. Pass the value through a typed/`external` wrapper function instead, since the `js` argument must be a literal constant.
saying these in an interview costs you the question
- Assigning a concrete value to `definedExternally`
- Using `definedExternally` outside an `external` declaration
- Trying to interpolate Kotlin variables into a `js("...")` string
- Letting the `dynamic` from `js(...)` propagate untyped
- Expecting the compiler to validate the JS inside `js(...)`