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
71// Null
72#[wasm_bindgen(wasm_bindgen = crate)]
73extern "C" {
74    /// The JavaScript `null` value.
75    ///
76    /// This type represents the JavaScript `null` primitive value and can be
77    /// used as a generic type parameter to indicate that a value is `null`.
78    #[wasm_bindgen(is_type_of = JsValue::is_null, typescript_type = "null", no_upcast)]
79    #[derive(Clone, PartialEq)]
80    pub type Null;
81}
82
83impl Null {
84    /// The null constant.
85    pub const NULL: Null = Self { obj: JsValue::NULL };
86}
87
88impl Eq for Null {}
89
90impl Default for Null {
91    fn default() -> Self {
92        Self::NULL
93    }
94}
95
96impl fmt::Debug for Null {
97    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98        f.write_str("null")
99    }
100}
101
102impl fmt::Display for Null {
103    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104        f.write_str("null")
105    }
106}
107
108impl UpcastFrom<Null> for Null {}
109impl UpcastFrom<Null> for JsValue {}
110
111// JsOption
112#[wasm_bindgen(wasm_bindgen = crate)]
113extern "C" {
114    /// An optional JS value of type `T`.
115    ///
116    /// Unlike `Option<T>`, which is a Rust-side construct, `JsOption<T>` represents
117    /// a JS value that may be `T` or `undefined`, where the presence status is
118    /// not yet known in Rust. The value remains in JS until inspected via methods
119    /// like [`is_empty`](Self::is_empty), [`as_option`](Self::as_option), or
120    /// [`into_option`](Self::into_option).
121    ///
122    /// Only `undefined` is treated as absent, matching TypeScript's `T | undefined`.
123    /// JavaScript `null` is a distinct present value.
124    ///
125    /// `T` must implement [`JsGeneric`], meaning it is any type that can be
126    /// represented as a `JsValue` (e.g., `JsString`, `Number`, `Object`, etc.).
127    /// `JsOption<T>` itself implements `JsGeneric`, so it can be used in all
128    /// generic positions that accept JS types.
129    #[wasm_bindgen(typescript_type = "any", no_upcast)]
130    #[derive(Clone, PartialEq)]
131    pub type JsOption<T>;
132}
133
134impl<T: JsGeneric> JsOption<T> {
135    /// Creates an empty `JsOption<T>` representing `undefined`.
136    #[inline]
137    pub fn new() -> Self {
138        Undefined::UNDEFINED.unchecked_into()
139    }
140
141    /// Wraps a value in a `JsOption<T>`.
142    #[inline]
143    pub fn wrap(val: T) -> Self {
144        val.unchecked_into()
145    }
146
147    /// Creates a `JsOption<T>` from an `Option<T>`.
148    ///
149    /// Returns `JsOption::wrap(val)` if `Some(val)`, otherwise `JsOption::new()`.
150    #[inline]
151    pub fn from_option(opt: Option<T>) -> Self {
152        match opt {
153            Some(val) => Self::wrap(val),
154            None => Self::new(),
155        }
156    }
157
158    /// Tests whether this `JsOption<T>` is empty (`undefined`).
159    #[inline]
160    pub fn is_empty(&self) -> bool {
161        JsValue::is_undefined(self)
162    }
163
164    /// Converts this `JsOption<T>` to an `Option<T>` by cloning the inner value.
165    ///
166    /// Returns `None` if the value is `undefined`, otherwise returns
167    /// `Some(T)` with a clone of the contained value.
168    #[inline]
169    pub fn as_option(&self) -> Option<T> {
170        if JsValue::is_undefined(self) {
171            None
172        } else {
173            let cloned = self.deref().clone();
174            Some(cloned.unchecked_into())
175        }
176    }
177
178    /// Converts this `JsOption<T>` into an `Option<T>`, consuming `self`.
179    ///
180    /// Returns `None` if the value is `undefined`, otherwise returns
181    /// `Some(T)` with the contained value.
182    #[inline]
183    pub fn into_option(self) -> Option<T> {
184        if JsValue::is_undefined(&self) {
185            None
186        } else {
187            Some(self.unchecked_into())
188        }
189    }
190
191    /// Returns the contained value, consuming `self`.
192    ///
193    /// # Panics
194    ///
195    /// Panics if the value is `undefined`.
196    #[inline]
197    pub fn unwrap(self) -> T {
198        self.expect("called `JsOption::unwrap()` on an empty value")
199    }
200
201    /// Returns the contained value, consuming `self`.
202    ///
203    /// # Panics
204    ///
205    /// Panics if the value is `undefined`, with a panic message
206    /// including the passed message.
207    #[inline]
208    pub fn expect(self, msg: &str) -> T {
209        match self.into_option() {
210            Some(val) => val,
211            None => panic!("{}", msg),
212        }
213    }
214
215    /// Returns the contained value or a default.
216    ///
217    /// Returns the contained value if not `undefined`, otherwise
218    /// returns the default value of `T`.
219    #[inline]
220    pub fn unwrap_or_default(self) -> T
221    where
222        T: Default,
223    {
224        self.into_option().unwrap_or_default()
225    }
226
227    /// Returns the contained value or computes it from a closure.
228    ///
229    /// Returns the contained value if not `undefined`, otherwise
230    /// calls `f` and returns the result.
231    #[inline]
232    pub fn unwrap_or_else<F>(self, f: F) -> T
233    where
234        F: FnOnce() -> T,
235    {
236        self.into_option().unwrap_or_else(f)
237    }
238}
239
240impl<T: JsGeneric> Default for JsOption<T> {
241    fn default() -> Self {
242        Self::new()
243    }
244}
245
246impl<T: JsGeneric + fmt::Debug> fmt::Debug for JsOption<T> {
247    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
248        write!(f, "{}?(", core::any::type_name::<T>())?;
249        match self.as_option() {
250            Some(v) => write!(f, "{v:?}")?,
251            None => f.write_str("undefined")?,
252        }
253        f.write_str(")")
254    }
255}
256
257impl<T: JsGeneric + fmt::Display> fmt::Display for JsOption<T> {
258    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
259        write!(f, "{}?(", core::any::type_name::<T>())?;
260        match self.as_option() {
261            Some(v) => write!(f, "{v}")?,
262            None => f.write_str("undefined")?,
263        }
264        f.write_str(")")
265    }
266}
267
268impl UpcastFrom<JsValue> for JsOption<JsValue> {}
269impl<T> UpcastFrom<Undefined> for JsOption<T> {}
270impl<T> UpcastFrom<()> for JsOption<T> {}
271impl<T> UpcastFrom<JsOption<T>> for JsValue {}
272impl<T, U> UpcastFrom<JsOption<U>> for JsOption<T> where T: UpcastFrom<U> {}