What is the `dynamic` type in Kotlin/JS, and how does it change the compiler's behavior compared with a normal typed value?
answer
- dynamic = type-checker OFF
- Kotlin/JS only — not JVM/Native
- Any member/call compiles; resolved at runtime
- Operations return dynamic (infectious)
- Pin back with unsafeCast<T>()
basics
~20 sdynamic is a special Kotlin/JS type that turns off compile-time checks. You can read any property or call any method on it and the compiler won't complain — correctness is your responsibility, checked only at runtime by JavaScript.
solid answer
~40 s`dynamic` is a type available only in Kotlin/JS (not on JVM/Native) that opts a value **out** of Kotlin's type system. Any member access, method call, or operator on a `dynamic` value compiles without checking — it's emitted verbatim as a JS member access, and resolution happens at runtime. Assigning `dynamic` to a typed variable, or a typed value to `dynamic`, is allowed implicitly. Results of operations on `dynamic` are themselves `dynamic` (it's 'infectious'). It's the escape hatch for genuinely free-form JS — JSON blobs, loosely-typed libraries, or `js("...")` results. The cost: no autocomplete, no null-safety, no signature verification; typos become runtime `TypeError`s. Idiomatic Kotlin/JS prefers `external` typed facades and confines `dynamic` to a thin boundary, casting back to real types (often via `unsafeCast`) as soon as possible.
code
kotlin · 11 linesfun parse(raw: dynamic) {
// compiles no matter what — runtime decides
val first: String = raw.users[0].name
val n: Int = raw.count
// narrow back to a real type ASAP
val typed = raw.unsafeCast<List<dynamic>>()
}
// dynamic spreads:
val a: dynamic = js("42")
val b = a + 1 // b is also dynamic, not Intgo deeper
Knows dynamic disables type checks and is JS-only.
Explains implicit conversions, the infectious result type, and runtime-only resolution.
Confines dynamic to a boundary, narrows with unsafeCast, and articulates the tradeoff versus external facades.
Sets team policy on where dynamic is acceptable, how to wrap third-party JS safely, and the maintainability/error-surface implications.
## What `dynamic` is `dynamic` is a built-in type that exists **only in Kotlin/JS**. (Use it on JVM or Native and the code won't compile.) Declaring something `dynamic` tells the compiler: *stop type-checking this value entirely.* It behaves like JavaScript's untyped values. ```kotlin val obj: dynamic = js("({ name: 'Ada', age: 36 })") console.log(obj.name) // no checking — compiles console.log(obj.whatever.deep) // also compiles; fails only at runtime if wrong obj.doStuff(1, 2, 3) // any call shape is allowed ``` ## How it changes the compiler - **No member checks.** `value.foo`, `value.bar()`, `value[i]`, even operators are accepted regardless of whether they exist. The compiler emits the corresponding JS and lets the JS runtime resolve it. - **No null safety.** `dynamic` sits outside the nullable/non-null distinction; you get no `?.` enforcement. - **Implicit conversions both ways.** A typed value can flow into a `dynamic`, and a `dynamic` can be assigned to a statically typed variable **without a cast** — the compiler trusts you. - **Infectious results.** The result type of most operations on a `dynamic` is again `dynamic`, so it spreads unless you pin it back to a real type. - **Some things still apply.** `null`/comparison semantics and a few language constructs aren't fully bypassed, but for member access it's effectively 'anything goes'. ## Pinning back to typed code Because `dynamic` removes all safety, idiomatic code narrows it quickly. The stdlib offers `unsafeCast<T>()` to reinterpret a value as a concrete type with **zero runtime check** (you assert the shape): ```kotlin external interface Person { val name: String } val p: Person = obj.unsafeCast<Person>() ``` ## When to use it - Free-form JSON / config objects whose shape you don't model. - Quick interop with a loosely typed JS library before writing a proper `external` facade. - The result of `js("...")` inline JavaScript. ## `external` vs `dynamic` `external` keeps **full** type checking against signatures you declare; `dynamic` **removes** it. Prefer `external` facades; keep `dynamic` to a thin, well-commented boundary so typos don't silently become runtime `TypeError`s deep in your app.
- Can you use `dynamic` in a Kotlin/JVM module?No. `dynamic` is only valid in Kotlin/JS targets; it won't compile elsewhere, which matters in multiplatform common code.
- What is `unsafeCast` and how does it differ from a normal `as` cast?`unsafeCast<T>()` reinterprets a value as `T` with no runtime type check, unlike `as`, which can throw `ClassCastException`. It's used to leave `dynamic` for a real type when you're sure of the shape.
Like casting everything to any in TypeScript: the compiler stops arguing and you own every mistake.
saying these in an interview costs you the question
- Claiming `dynamic` works in common/JVM code
- Using `dynamic` everywhere instead of `external` facades
- Forgetting that operations on `dynamic` yield `dynamic`
- Expecting null-safety or autocomplete on a `dynamic`
- Thinking the compiler validates member access on `dynamic`