Skip to main content

wasm_bindgen/
sys.rs

1//! JavaScript system types that are re-exported by `js-sys`.
2//!
3//! These types represent fundamental JavaScript values and are designed to be
4//! used as generic type parameters in typed JavaScript collections and APIs.
5use crate::convert::UpcastFrom;
6use crate::JsCast;
7use crate::JsGeneric;
8use crate::JsValue;
9use core::fmt;
10use core::ops::Deref;
11use wasm_bindgen_macro::wasm_bindgen;
12
13/// Marker trait for types which represent `Resolution` or `Promise<Resolution>`.
14///
15/// For all types except for `Promise`, `Resolution` is equal to the type itself.
16/// For `Promise` or any thenable or type extending Promise, `Resolution` is the
17/// type of the promise resolution.
18///
19/// Manually implementing this trait is only required for custom thenables or
20/// types which extend Promise. To disable automatic implementation, use the
21/// `#[wasm_bindgen(no_promising)]` attribute.
22pub trait Promising {
23    /// The type that this value resolves to.
24    type Resolution;
25}
26
27// Undefined
28#[wasm_bindgen(wasm_bindgen = crate)]
29extern "C" {
30    /// The JavaScript `undefined` value.
31    ///
32    /// This type represents the JavaScript `undefined` primitive value and can be
33    /// used as a generic type parameter to indicate that a value is `undefined`.
34    #[wasm_bindgen(is_type_of = JsValue::is_undefined, typescript_type = "undefined", no_upcast)]
35    #[derive(Clone, PartialEq)]
36    pub type Undefined;
37}
38
39impl Undefined {
40    /// The undefined constant.
41    pub const UNDEFINED: Undefined = Self {
42        obj: JsValue::UNDEFINED,
43    };
44}
45
46impl Eq for Undefined {}
47
48impl Default for Undefined {
49    fn default() -> Self {
50        Self::UNDEFINED
51    }
52}
53
54impl fmt::Debug for Undefined {
55    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56        f.write_str("undefined")
57    }
58}
59
60impl fmt::Display for Undefined {
61    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62        f.write_str("undefined")
63    }
64}
65
66impl UpcastFrom<Undefined> for Undefined {}
67impl UpcastFrom<()> for Undefined {}
68impl UpcastFrom<Undefined> for () {}
69impl UpcastFrom<Undefined> for JsValue {}
70// An `Option<T>` reads `undefined` as `None`, so `undefined` is one of its
71// values. This also lets a closure taking a trailing `Option<T>` stand in for
72// one without it.
73impl<T> UpcastFrom<Undefined> for Option<T> {}
74
75// Null
76#[wasm_bindgen(wasm_bindgen = crate)]
77extern "C" {
78    /// The JavaScript `null` value.
79    ///
80    /// This type represents the JavaScript `null` primitive value and can be
81    /// used as a generic type parameter to indicate that a value is `null`.
82    #[wasm_bindgen(is_type_of = JsValue::is_null, typescript_type = "null", no_upcast)]
83    #[derive(Clone, PartialEq)]
84    pub type Null;
85}
86
87impl Null {
88    /// The null constant.
89    pub const NULL: Null = Self { obj: JsValue::NULL };
90}
91
92impl Eq for Null {}
93
94impl Default for Null {
95    fn default() -> Self {
96        Self::NULL
97    }
98}
99
100impl fmt::Debug for Null {
101    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102        f.write_str("null")
103    }
104}
105
106impl fmt::Display for Null {
107    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108        f.write_str("null")
109    }
110}
111
112impl UpcastFrom<Null> for Null {}
113impl UpcastFrom<Null> for JsValue {}
114// `null` is `None` for an `Option<T>`, or `Some(Nullable::Null)` when `T` is a
115// `Nullable`, and `Nullable::Null` for a `Nullable<T>`. `undefined` is not a
116// `Nullable<T>`, as it goes to `T`'s own conversion.
117impl<T> UpcastFrom<Null> for Option<T> {}
118impl<T> UpcastFrom<Null> for crate::Nullable<T> {}
119
120// JsOption
121#[wasm_bindgen(wasm_bindgen = crate)]
122extern "C" {
123    /// An optional JS value of type `T`.
124    ///
125    /// Unlike `Option<T>`, which is a Rust-side construct, `JsOption<T>` represents
126    /// a JS value that may be `T` or `undefined`, where the presence status is
127    /// not yet known in Rust. The value remains in JS until inspected via methods
128    /// like [`is_empty`](Self::is_empty), [`as_option`](Self::as_option), or
129    /// [`into_option`](Self::into_option).
130    ///
131    /// Only `undefined` is treated as absent, matching TypeScript's `T | undefined`.
132    /// JavaScript `null` is a distinct present value.
133    ///
134    /// `T` must implement [`JsGeneric`], meaning it is any type that can be
135    /// represented as a `JsValue` (e.g., `JsString`, `Number`, `Object`, etc.).
136    /// `JsOption<T>` itself implements `JsGeneric`, so it can be used in all
137    /// generic positions that accept JS types.
138    // Next major: deprecate in favour of `Option<T>`.
139    #[wasm_bindgen(typescript_type = "any", no_upcast)]
140    #[derive(Clone, PartialEq)]
141    pub type JsOption<T>;
142}
143
144impl<T: JsGeneric> JsOption<T> {
145    /// Creates an empty `JsOption<T>` representing `undefined`.
146    #[inline]
147    pub fn new() -> Self {
148        Undefined::UNDEFINED.unchecked_into()
149    }
150
151    /// Wraps a value in a `JsOption<T>`.
152    #[inline]
153    pub fn wrap(val: T) -> Self {
154        val.unchecked_into()
155    }
156
157    /// Creates a `JsOption<T>` from an `Option<T>`.
158    ///
159    /// Returns `JsOption::wrap(val)` if `Some(val)`, otherwise `JsOption::new()`.
160    #[inline]
161    pub fn from_option(opt: Option<T>) -> Self {
162        match opt {
163            Some(val) => Self::wrap(val),
164            None => Self::new(),
165        }
166    }
167
168    /// Tests whether this `JsOption<T>` is empty (`undefined`).
169    #[inline]
170    pub fn is_empty(&self) -> bool {
171        JsValue::is_undefined(self)
172    }
173
174    /// Converts this `JsOption<T>` to an `Option<T>` by cloning the inner value.
175    ///
176    /// Returns `None` if the value is `undefined`, otherwise returns
177    /// `Some(T)` with a clone of the contained value.
178    #[inline]
179    pub fn as_option(&self) -> Option<T> {
180        if JsValue::is_undefined(self) {
181            None
182        } else {
183            let cloned = self.deref().clone();
184            Some(cloned.unchecked_into())
185        }
186    }
187
188    /// Converts this `JsOption<T>` into an `Option<T>`, consuming `self`.
189    ///
190    /// Returns `None` if the value is `undefined`, otherwise returns
191    /// `Some(T)` with the contained value.
192    #[inline]
193    pub fn into_option(self) -> Option<T> {
194        if JsValue::is_undefined(&self) {
195            None
196        } else {
197            Some(self.unchecked_into())
198        }
199    }
200
201    /// Returns the contained value, consuming `self`.
202    ///
203    /// # Panics
204    ///
205    /// Panics if the value is `undefined`.
206    #[inline]
207    pub fn unwrap(self) -> T {
208        self.expect("called `JsOption::unwrap()` on an empty value")
209    }
210
211    /// Returns the contained value, consuming `self`.
212    ///
213    /// # Panics
214    ///
215    /// Panics if the value is `undefined`, with a panic message
216    /// including the passed message.
217    #[inline]
218    pub fn expect(self, msg: &str) -> T {
219        match self.into_option() {
220            Some(val) => val,
221            None => panic!("{}", msg),
222        }
223    }
224
225    /// Returns the contained value or a default.
226    ///
227    /// Returns the contained value if not `undefined`, otherwise
228    /// returns the default value of `T`.
229    #[inline]
230    pub fn unwrap_or_default(self) -> T
231    where
232        T: Default,
233    {
234        self.into_option().unwrap_or_default()
235    }
236
237    /// Returns the contained value or computes it from a closure.
238    ///
239    /// Returns the contained value if not `undefined`, otherwise
240    /// calls `f` and returns the result.
241    #[inline]
242    pub fn unwrap_or_else<F>(self, f: F) -> T
243    where
244        F: FnOnce() -> T,
245    {
246        self.into_option().unwrap_or_else(f)
247    }
248}
249
250impl<T: JsGeneric> Default for JsOption<T> {
251    fn default() -> Self {
252        Self::new()
253    }
254}
255
256impl<T: JsGeneric + fmt::Debug> fmt::Debug for JsOption<T> {
257    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
258        write!(f, "{}?(", core::any::type_name::<T>())?;
259        match self.as_option() {
260            Some(v) => write!(f, "{v:?}")?,
261            None => f.write_str("undefined")?,
262        }
263        f.write_str(")")
264    }
265}
266
267impl<T: JsGeneric + fmt::Display> fmt::Display for JsOption<T> {
268    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
269        write!(f, "{}?(", core::any::type_name::<T>())?;
270        match self.as_option() {
271            Some(v) => write!(f, "{v}")?,
272            None => f.write_str("undefined")?,
273        }
274        f.write_str(")")
275    }
276}
277
278impl UpcastFrom<JsValue> for JsOption<JsValue> {}
279impl<T> UpcastFrom<Undefined> for JsOption<T> {}
280impl<T> UpcastFrom<()> for JsOption<T> {}
281impl<T> UpcastFrom<JsOption<T>> for JsValue {}
282impl<T, U> UpcastFrom<JsOption<U>> for JsOption<T> where T: UpcastFrom<U> {}
283
284// JsNullable
285#[wasm_bindgen(wasm_bindgen = crate)]
286extern "C" {
287    /// A nullable JS value of type `T`.
288    ///
289    /// Unlike `Option<T>`, which is a Rust-side construct, `JsNullable<T>`
290    /// represents a JS value that may be `T` or `null`, where the presence
291    /// status is not yet known in Rust. The value remains in JS until inspected
292    /// via methods like [`is_empty`](Self::is_empty),
293    /// [`as_option`](Self::as_option), or [`into_option`](Self::into_option).
294    ///
295    /// This models WebIDL nullable types (`T?`). Following WebIDL's
296    /// ECMAScript conversion rules, where `undefined` coerces to `null` at
297    /// nullable positions, both `null` and `undefined` are treated as absent.
298    /// The canonical empty value produced from Rust is `null`.
299    ///
300    /// For TypeScript-style `T | undefined` with strict undefined-only
301    /// emptiness, use [`JsOption<T>`] instead.
302    ///
303    /// `T` must implement [`JsGeneric`], meaning it is any type that can be
304    /// represented as a `JsValue` (e.g., `JsString`, `Number`, `Object`, etc.).
305    /// `JsNullable<T>` itself implements `JsGeneric`, so it can be used in all
306    /// generic positions that accept JS types.
307    // Next major: deprecate in favour of `Nullable<T>`.
308    #[wasm_bindgen(typescript_type = "any", no_upcast)]
309    #[derive(Clone, PartialEq)]
310    pub type JsNullable<T>;
311}
312
313impl<T: JsGeneric> JsNullable<T> {
314    /// Creates an empty `JsNullable<T>` representing `null`.
315    #[inline]
316    pub fn new() -> Self {
317        Null::NULL.unchecked_into()
318    }
319
320    /// Wraps a value in a `JsNullable<T>`.
321    #[inline]
322    pub fn wrap(val: T) -> Self {
323        val.unchecked_into()
324    }
325
326    /// Creates a `JsNullable<T>` from an `Option<T>`.
327    ///
328    /// Returns `JsNullable::wrap(val)` if `Some(val)`, otherwise `JsNullable::new()`.
329    #[inline]
330    pub fn from_option(opt: Option<T>) -> Self {
331        match opt {
332            Some(val) => Self::wrap(val),
333            None => Self::new(),
334        }
335    }
336
337    /// Tests whether this `JsNullable<T>` is empty (`null` or `undefined`).
338    #[inline]
339    pub fn is_empty(&self) -> bool {
340        JsValue::is_null(self) || JsValue::is_undefined(self)
341    }
342
343    /// Converts this `JsNullable<T>` to an `Option<T>` by cloning the inner value.
344    ///
345    /// Returns `None` if the value is `null` or `undefined`, otherwise returns
346    /// `Some(T)` with a clone of the contained value.
347    #[inline]
348    pub fn as_option(&self) -> Option<T> {
349        if self.is_empty() {
350            None
351        } else {
352            let cloned = self.deref().clone();
353            Some(cloned.unchecked_into())
354        }
355    }
356
357    /// Converts this `JsNullable<T>` into an `Option<T>`, consuming `self`.
358    ///
359    /// Returns `None` if the value is `null` or `undefined`, otherwise returns
360    /// `Some(T)` with the contained value.
361    #[inline]
362    pub fn into_option(self) -> Option<T> {
363        if self.is_empty() {
364            None
365        } else {
366            Some(self.unchecked_into())
367        }
368    }
369
370    /// Returns the contained value, consuming `self`.
371    ///
372    /// # Panics
373    ///
374    /// Panics if the value is `null` or `undefined`.
375    #[inline]
376    pub fn unwrap(self) -> T {
377        self.expect("called `JsNullable::unwrap()` on an empty value")
378    }
379
380    /// Returns the contained value, consuming `self`.
381    ///
382    /// # Panics
383    ///
384    /// Panics if the value is `null` or `undefined`, with a panic message
385    /// including the passed message.
386    #[inline]
387    pub fn expect(self, msg: &str) -> T {
388        match self.into_option() {
389            Some(val) => val,
390            None => panic!("{}", msg),
391        }
392    }
393
394    /// Returns the contained value or a default.
395    ///
396    /// Returns the contained value if not `null` or `undefined`, otherwise
397    /// returns the default value of `T`.
398    #[inline]
399    pub fn unwrap_or_default(self) -> T
400    where
401        T: Default,
402    {
403        self.into_option().unwrap_or_default()
404    }
405
406    /// Returns the contained value or computes it from a closure.
407    ///
408    /// Returns the contained value if not `null` or `undefined`, otherwise
409    /// calls `f` and returns the result.
410    #[inline]
411    pub fn unwrap_or_else<F>(self, f: F) -> T
412    where
413        F: FnOnce() -> T,
414    {
415        self.into_option().unwrap_or_else(f)
416    }
417}
418
419impl<T: JsGeneric> Default for JsNullable<T> {
420    fn default() -> Self {
421        Self::new()
422    }
423}
424
425impl<T: JsGeneric + fmt::Debug> fmt::Debug for JsNullable<T> {
426    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
427        write!(f, "{}?(", core::any::type_name::<T>())?;
428        match self.as_option() {
429            Some(v) => write!(f, "{v:?}")?,
430            None => f.write_str("null")?,
431        }
432        f.write_str(")")
433    }
434}
435
436impl<T: JsGeneric + fmt::Display> fmt::Display for JsNullable<T> {
437    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
438        write!(f, "{}?(", core::any::type_name::<T>())?;
439        match self.as_option() {
440            Some(v) => write!(f, "{v}")?,
441            None => f.write_str("null")?,
442        }
443        f.write_str(")")
444    }
445}
446
447impl UpcastFrom<JsValue> for JsNullable<JsValue> {}
448impl<T> UpcastFrom<Null> for JsNullable<T> {}
449impl<T> UpcastFrom<Undefined> for JsNullable<T> {}
450impl<T> UpcastFrom<()> for JsNullable<T> {}
451impl<T> UpcastFrom<JsNullable<T>> for JsValue {}
452impl<T, U> UpcastFrom<JsNullable<U>> for JsNullable<T> where T: UpcastFrom<U> {}
453impl<T, U> UpcastFrom<JsOption<U>> for JsNullable<T> where T: UpcastFrom<U> {}