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> {}