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> {}
273
274// JsNullable
275#[wasm_bindgen(wasm_bindgen = crate)]
276extern "C" {
277 /// A nullable JS value of type `T`.
278 ///
279 /// Unlike `Option<T>`, which is a Rust-side construct, `JsNullable<T>`
280 /// represents a JS value that may be `T` or `null`, where the presence
281 /// status is not yet known in Rust. The value remains in JS until inspected
282 /// via methods like [`is_empty`](Self::is_empty),
283 /// [`as_option`](Self::as_option), or [`into_option`](Self::into_option).
284 ///
285 /// This models WebIDL nullable types (`T?`). Following WebIDL's
286 /// ECMAScript conversion rules, where `undefined` coerces to `null` at
287 /// nullable positions, both `null` and `undefined` are treated as absent.
288 /// The canonical empty value produced from Rust is `null`.
289 ///
290 /// For TypeScript-style `T | undefined` with strict undefined-only
291 /// emptiness, use [`JsOption<T>`] instead.
292 ///
293 /// `T` must implement [`JsGeneric`], meaning it is any type that can be
294 /// represented as a `JsValue` (e.g., `JsString`, `Number`, `Object`, etc.).
295 /// `JsNullable<T>` itself implements `JsGeneric`, so it can be used in all
296 /// generic positions that accept JS types.
297 #[wasm_bindgen(typescript_type = "any", no_upcast)]
298 #[derive(Clone, PartialEq)]
299 pub type JsNullable<T>;
300}
301
302impl<T: JsGeneric> JsNullable<T> {
303 /// Creates an empty `JsNullable<T>` representing `null`.
304 #[inline]
305 pub fn new() -> Self {
306 Null::NULL.unchecked_into()
307 }
308
309 /// Wraps a value in a `JsNullable<T>`.
310 #[inline]
311 pub fn wrap(val: T) -> Self {
312 val.unchecked_into()
313 }
314
315 /// Creates a `JsNullable<T>` from an `Option<T>`.
316 ///
317 /// Returns `JsNullable::wrap(val)` if `Some(val)`, otherwise `JsNullable::new()`.
318 #[inline]
319 pub fn from_option(opt: Option<T>) -> Self {
320 match opt {
321 Some(val) => Self::wrap(val),
322 None => Self::new(),
323 }
324 }
325
326 /// Tests whether this `JsNullable<T>` is empty (`null` or `undefined`).
327 #[inline]
328 pub fn is_empty(&self) -> bool {
329 JsValue::is_null(self) || JsValue::is_undefined(self)
330 }
331
332 /// Converts this `JsNullable<T>` to an `Option<T>` by cloning the inner value.
333 ///
334 /// Returns `None` if the value is `null` or `undefined`, otherwise returns
335 /// `Some(T)` with a clone of the contained value.
336 #[inline]
337 pub fn as_option(&self) -> Option<T> {
338 if self.is_empty() {
339 None
340 } else {
341 let cloned = self.deref().clone();
342 Some(cloned.unchecked_into())
343 }
344 }
345
346 /// Converts this `JsNullable<T>` into an `Option<T>`, consuming `self`.
347 ///
348 /// Returns `None` if the value is `null` or `undefined`, otherwise returns
349 /// `Some(T)` with the contained value.
350 #[inline]
351 pub fn into_option(self) -> Option<T> {
352 if self.is_empty() {
353 None
354 } else {
355 Some(self.unchecked_into())
356 }
357 }
358
359 /// Returns the contained value, consuming `self`.
360 ///
361 /// # Panics
362 ///
363 /// Panics if the value is `null` or `undefined`.
364 #[inline]
365 pub fn unwrap(self) -> T {
366 self.expect("called `JsNullable::unwrap()` on an empty value")
367 }
368
369 /// Returns the contained value, consuming `self`.
370 ///
371 /// # Panics
372 ///
373 /// Panics if the value is `null` or `undefined`, with a panic message
374 /// including the passed message.
375 #[inline]
376 pub fn expect(self, msg: &str) -> T {
377 match self.into_option() {
378 Some(val) => val,
379 None => panic!("{}", msg),
380 }
381 }
382
383 /// Returns the contained value or a default.
384 ///
385 /// Returns the contained value if not `null` or `undefined`, otherwise
386 /// returns the default value of `T`.
387 #[inline]
388 pub fn unwrap_or_default(self) -> T
389 where
390 T: Default,
391 {
392 self.into_option().unwrap_or_default()
393 }
394
395 /// Returns the contained value or computes it from a closure.
396 ///
397 /// Returns the contained value if not `null` or `undefined`, otherwise
398 /// calls `f` and returns the result.
399 #[inline]
400 pub fn unwrap_or_else<F>(self, f: F) -> T
401 where
402 F: FnOnce() -> T,
403 {
404 self.into_option().unwrap_or_else(f)
405 }
406}
407
408impl<T: JsGeneric> Default for JsNullable<T> {
409 fn default() -> Self {
410 Self::new()
411 }
412}
413
414impl<T: JsGeneric + fmt::Debug> fmt::Debug for JsNullable<T> {
415 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
416 write!(f, "{}?(", core::any::type_name::<T>())?;
417 match self.as_option() {
418 Some(v) => write!(f, "{v:?}")?,
419 None => f.write_str("null")?,
420 }
421 f.write_str(")")
422 }
423}
424
425impl<T: JsGeneric + fmt::Display> fmt::Display 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 UpcastFrom<JsValue> for JsNullable<JsValue> {}
437impl<T> UpcastFrom<Null> for JsNullable<T> {}
438impl<T> UpcastFrom<Undefined> for JsNullable<T> {}
439impl<T> UpcastFrom<()> for JsNullable<T> {}
440impl<T> UpcastFrom<JsNullable<T>> for JsValue {}
441impl<T, U> UpcastFrom<JsNullable<U>> for JsNullable<T> where T: UpcastFrom<U> {}
442impl<T, U> UpcastFrom<JsOption<U>> for JsNullable<T> where T: UpcastFrom<U> {}