WebAssembly's Type System Just Got More Interesting: Nominal Types Explained
Source: lobsters
Andy Wingo, one of the more prolific voices on the WebAssembly specification, has a new deep-dive on nominal types in WebAssembly that’s worth sitting with if you care about where Wasm is heading.
The short version: WebAssembly’s GC proposal chose nominal typing over structural typing, and the implications of that choice ripple through everything from how your Kotlin or Dart code compiles down to bytes, to how fast runtime type checks can be.
Structural vs. Nominal: A Quick Recap
If you’ve spent time with TypeScript, you’re familiar with structural typing — two types are compatible if their shapes match:
type Point2D = { x: number; y: number };
type Coord = { x: number; y: number };
// These are interchangeable in TypeScript — same structure
Nominal typing flips this. Two types are only the same type if they share the same declaration — identity, not shape, is what matters. Java and C# work this way. Two classes with identical fields are still distinct types.
WebAssembly GC went nominal. That wasn’t a foregone conclusion; the spec explored isorecursive structural types for a while. But nominal won, largely because it maps directly to how most GC’d languages actually model their type hierarchies.
Why It Matters for Runtime Performance
One concrete payoff: nominal types make subtype checks cheap. When every type has a unique identity baked in at definition time, the runtime can use simple integer comparisons or bitset tricks to answer “is this object an instance of T?” instead of walking structural definitions.
For a VM shipping millions of Wasm modules, that’s not a theoretical win — it’s measurable latency on every cast and type guard.
The Compiler Author’s Perspective
Wingo’s post is especially useful because he writes from the perspective of someone who actually compiles real languages to Wasm (Guile Scheme, among others). The design decisions that look clean on a spec sheet often create sharp edges for compiler authors.
Nominal types in WasmGC mean language runtimes need to emit type definitions that precisely capture their own type hierarchies, without being able to lean on structural compatibility as a fallback. If you’re compiling a dynamically-typed language that uses structural duck-typing internally, you have to decide upfront how to represent that in a nominal world.
What This Means for the Ecosystem
The nominal type system is already the reality for WasmGC targets today — Kotlin, Dart, Java via J2Wasm, and others are all generating code that assumes it. The interesting open questions are:
- How do you share types across Wasm modules when each module defines its own nominal universe?
- What does the component model do with this at its boundaries?
- Can toolchains eventually get smart enough to deduplicate redundant type definitions?
Wingo’s post is the kind of thing I find myself re-reading a few times. The WebAssembly type system is genuinely interesting CS, not just plumbing, and posts like this are the best way to build intuition for where the platform is headed.