js-sys

The js-sys crate provides bindings to all JavaScript global APIs guaranteed to exist in every JavaScript environment by the ECMAScript standard.

Wasm-bindgen supports generic typing using type erasure for imported JavaScript types. Generic parameters exist only in Rust code—they are completely erased in the generated JavaScript bindings.

For a conceptual overview and usage guide, see Working with Generics.

When passing a typed value (e.g., Array<Number>) to a function expecting a wider type (e.g., &Array<JsValue>), use the upcast() method: my_array.upcast(). See Upcasting Types for details.

All generic types listed implement JsGeneric and default to JsValue when type parameters are unspecified.

TypeDescription
Array<T>Typed JavaScript array
ArrayTuple<T1, ..., T8>Array with per-item typing (up to 8)
Function<fn(A) -> R>Typed function with return and arguments
Promise<T>Promise resolving to T
Map<K, V>Typed map
Set<T>Typed set
Iterator<T> / AsyncIterator<T>Typed iterators
Generator<T> / AsyncGenerator<T>Typed generators
Object<T>Object with typed values
WeakMap<K, V> / WeakSet<T> / WeakRef<T>Weak collections
JsOption<T>Nullable JS value (T, null, or undefined)
NumberJavaScript number primitive
JsStringJavaScript string primitive
BooleanJavaScript boolean primitive
BigIntJavaScript BigInt primitive
SymbolJavaScript Symbol primitive
UndefinedJavaScript undefined value
NullJavaScript null value

Array<T>

Typed JavaScript Array builtin.

#![allow(unused)]
fn main() {
use js_sys::{Array, Number, JsString};

let numbers: Array<Number> = Array::new_typed();
numbers.push(&Number::from(42));

let first: Number = numbers.get(0);

for num in numbers.iter() {
    // num is Number
}
}

ArrayTuple<T1, ..., T8>

JavaScript Array with per-item typing, supporting up to 8 items.

#![allow(unused)]
fn main() {
use js_sys::{ArrayTuple, Number, JsString, Boolean};

let tuple: ArrayTuple<Number, JsString, Boolean> = ArrayTuple::new();
tuple.set0(&Number::from(1));
tuple.set1(&JsString::from("hello"));
tuple.set2(&Boolean::from(true));

let n: Number = tuple.get0();
let s: JsString = tuple.get1();
let b: Boolean = tuple.get2();
}

Function<fn(A) -> R>

Typed JavaScript function with return type and up to 8 arguments.

#![allow(unused)]
fn main() {
use js_sys::{Function, Number, JsString};

#[wasm_bindgen]
extern "C" {
    fn getFormatter() -> Function<fn(Number) -> JsString>;
}

let formatter = getFormatter();
let result: JsString = formatter.call1(&JsValue::UNDEFINED, &Number::from(42))?;
}

Type aliases

AliasDescription
TypedFunctionStrict arity checking (arguments default to None)
AnyFunctionLenient behavior (arguments default to JsValue)
VoidFunctionFunction<fn(...) -> Undefined> with strict arity

Calling with tuples

#![allow(unused)]
fn main() {
let f: Function<fn(JsString, Boolean) -> Number> = get_function();

// call accepts a tuple of references
let result = f.call(&context, ())?;                      // no args
let result = f.call(&context, (&my_string,))?;           // one arg
let result = f.call(&context, (&my_string, &my_bool))?;  // two args

// bindn returns a new function with arguments pre-bound
let bound: Function<fn(Boolean) -> Number> = f.bindn(&context, (&my_string,));

// Argument upcasting: pass subtypes where supertypes are expected
let f: Function<fn(JsValue, JsValue) -> Number> = get_function();
let result = f.call(&context, (&my_number, &my_string))?;  // Number, JsString upcast to JsValue
}

Converting from Closures

Use Function::from_closure() to convert owned Closure types to typed Function, and Function::closure_ref() for borrowed closures:

#![allow(unused)]
fn main() {
use js_sys::{Function, Number, JsString};
use wasm_bindgen::prelude::*;

// Owned closure to Function (transfers ownership to JS)
let closure: Closure<dyn FnMut(Number) -> JsString> = Closure::new(|n: Number| {
    JsString::from(format!("Value: {}", n.value_of()))
});
let func: Function<fn(Number) -> JsString> = Function::from_closure(closure);
}

For borrowed closures, use closure_ref:

#![allow(unused)]
fn main() {
let mut count = 0u32;
let mut increment = || {
    count += 1;
    Number::from(count)
};
let closure = ScopedClosure::borrow_mut(&mut increment);
let func: &Function<fn() -> Number> = Function::closure_ref(&closure);
}

Primitive type coercion

Rust primitives automatically coerce to JS types in closure return positions:

#![allow(unused)]
fn main() {
// u32 coerces to Number
let closure: Closure<dyn Fn() -> u32> = Closure::new(|| 42);
let func: Function<fn() -> Number> = Function::from_closure(closure);

// String coerces to JsString
let closure: Closure<dyn Fn() -> String> = Closure::new(|| "hello".to_string());
let func: Function<fn() -> JsString> = Function::from_closure(closure);
}

This works for all numeric primitives (u8, i32, f64, etc.) and string types (String, &str, char).

See Passing Rust Closures to JS for more details on closure types and lifecycle management.

Promise<T>

Typed JavaScript Promise that resolves to T.

#![allow(unused)]
fn main() {
use js_sys::{Promise, Number};

#[wasm_bindgen]
extern "C" {
    fn fetchCount() -> Promise<Number>;
}

let count: Number = JsFuture::from(fetchCount()).await?;
}

The Promising trait

The Promising trait represents types that are either T or Promise<T>. Use it to accept both immediate values and promises in function signatures:

#![allow(unused)]
fn main() {
use js_sys::{Promising, Promise, Number};

#[wasm_bindgen]
extern "C" {
    // Accepts either Number or Promise<Number>
    fn getValue<T: Promising<Resolution = Number>>(value: T) -> Promise<Number>;
}
}

Map<K, V>

Typed JavaScript Map builtin.

#![allow(unused)]
fn main() {
use js_sys::{Map, JsString, Number};

let map: Map<JsString, Number> = Map::new_typed();
map.set(&JsString::from("key"), &Number::from(100));

let value: Number = map.get(&JsString::from("key"));
}

Set<T>

Typed JavaScript Set builtin.

#![allow(unused)]
fn main() {
use js_sys::{Set, JsString};

let set: Set<JsString> = Set::new_typed();
set.add(&JsString::from("value"));

let has: bool = set.has(&JsString::from("value"));
}

Iterator<T> and AsyncIterator<T>

Typed iterators for synchronous and asynchronous iteration.

#![allow(unused)]
fn main() {
use js_sys::{Iterator, Number};

#[wasm_bindgen]
extern "C" {
    fn getNumberIterator() -> Iterator<Number>;
}

let iter = getNumberIterator();
while let Some(num) = iter.next() {
    // num is Number
}
}

The Iterable and AsyncIterable traits

The Iterable and AsyncIterable traits represent objects implementing the iterator protocol via [Symbol.iterator]() or [Symbol.asyncIterator](). Use them to accept any iterable type in function signatures:

#![allow(unused)]
fn main() {
use js_sys::{Iterable, AsyncIterable, Number};

#[wasm_bindgen]
extern "C" {
    // Accepts Array, Set, Generator, or any other iterable
    fn processIterable<T: Iterable<Item = Number>>(items: T);
    
    // Accepts AsyncGenerator or any other async iterable
    fn processAsyncIterable<T: AsyncIterable<Item = Number>>(items: T);
}
}

Generator<T> and AsyncGenerator<T>

Typed generator builtins.

#![allow(unused)]
fn main() {
use js_sys::{Generator, Number};

#[wasm_bindgen]
extern "C" {
    fn createGenerator() -> Generator<Number>;
}

let gen = createGenerator();
let result = gen.next();
}

Object<T>

Typed object records where values are of type T.

#![allow(unused)]
fn main() {
use js_sys::{Object, Number};

let obj: Object<Number> = Object::new_typed();
}

WeakMap<K, V>, WeakSet<T>, WeakRef<T>

Typed weak collection builtins.

#![allow(unused)]
fn main() {
use js_sys::{WeakMap, WeakSet, WeakRef, Object, Number};

let weak_map: WeakMap<Object, Number> = WeakMap::new_typed();
let weak_set: WeakSet<Object> = WeakSet::new_typed();
let weak_ref: WeakRef<Object> = WeakRef::new(&my_object);
}

JsOption<T>

Represents a JS value that may be T, null, or undefined.

#![allow(unused)]
fn main() {
use wasm_bindgen::JsOption;
use js_sys::Number;

#[wasm_bindgen]
extern "C" {
    fn maybeGetValue() -> JsOption<Number>;
}

let value = maybeGetValue();

// Check emptiness
if value.is_empty() {
    // null or undefined
}

// Convert to Option
match value.into_option() {
    Some(num) => { /* use num */ }
    None => { /* handle null */ }
}

// Unwrap methods
let num = value.unwrap();
let num = value.expect("should exist");
let num = value.unwrap_or_default();
let num = value.unwrap_or_else(|| Number::from(0));

// Create values
let with_value = JsOption::wrap(Number::from(42));
let empty: JsOption<Number> = JsOption::new();
let from_opt = JsOption::from_option(Some(Number::from(42)));
}

JsOption<T> vs Option<T>:

TypeBehavior
Option<T>Undefined or Null check at ABI boundary, immediately converts to Some(T) or None
JsOption<T>Defers undefined or null check, works in JsGeneric positions