Value Types
WebAssembly is a typed stack machine. Every value on the stack has a concrete type, and every function declares exactly what types it consumes and produces. But unlike most languages, Wasm has a very small set of types to choose from.
The four value types
Section titled “The four value types”WebAssembly has exactly four value types:
| Type | Width | Kind |
|---|---|---|
i32 | 32-bit | Integer |
i64 | 64-bit | Integer |
f32 | 32-bit | Float |
f64 | 64-bit | Float |
There are no strings, booleans, arrays, or objects at the Wasm level. Everything is a number. Boolean logic is represented with i32 values where 0 means false and any non-zero value means true.
Pushing constants onto the stack
Section titled “Pushing constants onto the stack”Each type has a const instruction that pushes a literal value:
(module (func (export "answer") (result i32) i32.const 42))For a 64-bit float:
(module (func (export "pi") (result f64) f64.const 3.14159265358979))And for a 64-bit integer — note that i64 is special when crossing into JavaScript:
(module (func (export "bignum") (result i64) i64.const 1000000000000))i64 and JavaScript BigInt
Section titled “i64 and JavaScript BigInt”JavaScript’s number type is a 64-bit float, which can only represent integers exactly up to 2^53. An i64 can hold values far beyond that. To preserve the full precision, the WebAssembly JavaScript API converts i64 return values to BigInt rather than number. This means typeof result === 'bigint' — not 'number'.
You can verify this in the runner below.
Trade-offs
Section titled “Trade-offs”| Option | Benefit | Cost |
|---|---|---|
| Writing WAT by hand | You see clearly that every value on the stack has a fixed type (i32, i64, f32, f64), giving a detailed feel for Wasm’s type system | You must always pick the matching const/instruction yourself; get the type wrong and validation fails immediately |
| Letting a compiler generate Wasm (Rust/AssemblyScript) | The compiler maps types from the source language to i32/i64/f32/f64 automatically, reducing type mistakes | You have to trust the compiler’s conversions, like i64 becoming a BigInt, without seeing exactly what happens in between |
Common mistakes
Section titled “Common mistakes”- Pushing a value with the wrong-typed instruction — e.g. using
i32.constwhen the function declares(result i64)— so what’s left on the stack at the end doesn’t match the signature and validation fails - Pushing constants in the wrong order before a computation instruction, producing a wrong result even though every pushed value has the correct type
- Forgetting that JavaScript must receive an
i64as aBigInt, not anumber, and then comparing or computing with it mixed directly with a regular number, causing a runtimeTypeError
💡 Real-world example
The official WebAssembly spec testsuite — mirrored for convenience by tools like WABT — contains many hand-written
.watfiles covering every value type (i32,i64,f32,f64) to verify encoders/decoders interpret each one exactly per spec, including the case wherei64must convert toBigInton the JavaScript side.