wasm_bindgen/
cast.rs

1use crate::{describe::WasmDescribe, JsValue};
2
3/// A trait for checked and unchecked casting between JS types.
4///
5/// Specified [in an RFC][rfc] this trait is intended to provide support for
6/// casting JS values between different types of one another. In JS there aren't
7/// many static types but we've ascribed JS values with static types in Rust,
8/// yet they often need to be switched to other types temporarily! This trait
9/// provides both checked and unchecked casting into various kinds of values.
10///
11/// This trait is automatically implemented for any type imported in a
12/// `#[wasm_bindgen]` `extern` block.
13///
14/// [rfc]: https://github.com/rustwasm/rfcs/blob/master/text/002-wasm-bindgen-inheritance-casting.md
15pub trait JsCast
16where
17    Self: AsRef<JsValue> + Into<JsValue>,
18{
19    /// Test whether this JS value has a type `T`.
20    ///
21    /// This method will dynamically check to see if this JS object can be
22    /// casted to the JS object of type `T`. Usually this uses the `instanceof`
23    /// operator. This also works with primitive types like
24    /// booleans/strings/numbers as well as cross-realm object like `Array`
25    /// which can originate from other iframes.
26    ///
27    /// In general this is intended to be a more robust version of
28    /// `is_instance_of`, but if you want strictly the `instanceof` operator
29    /// it's recommended to use that instead.
30    fn has_type<T>(&self) -> bool
31    where
32        T: JsCast,
33    {
34        T::is_type_of(self.as_ref())
35    }
36
37    /// Performs a dynamic cast (checked at runtime) of this value into the
38    /// target type `T`.
39    ///
40    /// This method will return `Err(self)` if `self.has_type::<T>()`
41    /// returns `false`, and otherwise it will return `Ok(T)` manufactured with
42    /// an unchecked cast (verified correct via the `has_type` operation).
43    fn dyn_into<T>(self) -> Result<T, Self>
44    where
45        T: JsCast,
46    {
47        if self.has_type::<T>() {
48            Ok(self.unchecked_into())
49        } else {
50            Err(self)
51        }
52    }
53
54    /// Performs a dynamic cast (checked at runtime) of this value into the
55    /// target type `T`.
56    ///
57    /// This method will return `None` if `self.has_type::<T>()`
58    /// returns `false`, and otherwise it will return `Some(&T)` manufactured
59    /// with an unchecked cast (verified correct via the `has_type` operation).
60    fn dyn_ref<T>(&self) -> Option<&T>
61    where
62        T: JsCast,
63    {
64        if self.has_type::<T>() {
65            Some(self.unchecked_ref())
66        } else {
67            None
68        }
69    }
70
71    /// Performs a zero-cost unchecked cast into the specified type.
72    ///
73    /// This method will convert the `self` value to the type `T`, where both
74    /// `self` and `T` are simple wrappers around `JsValue`. This method **does
75    /// not check whether `self` is an instance of `T`**. If used incorrectly
76    /// then this method may cause runtime exceptions in both Rust and JS, this
77    /// should be used with caution.
78    fn unchecked_into<T>(self) -> T
79    where
80        T: JsCast,
81    {
82        T::unchecked_from_js(self.into())
83    }
84
85    /// Performs a zero-cost unchecked cast into a reference to the specified
86    /// type.
87    ///
88    /// This method will convert the `self` value to the type `T`, where both
89    /// `self` and `T` are simple wrappers around `JsValue`. This method **does
90    /// not check whether `self` is an instance of `T`**. If used incorrectly
91    /// then this method may cause runtime exceptions in both Rust and JS, this
92    /// should be used with caution.
93    ///
94    /// This method, unlike `unchecked_into`, does not consume ownership of
95    /// `self` and instead works over a shared reference.
96    fn unchecked_ref<T>(&self) -> &T
97    where
98        T: JsCast,
99    {
100        T::unchecked_from_js_ref(self.as_ref())
101    }
102
103    /// Test whether this JS value is an instance of the type `T`.
104    ///
105    /// This method performs a dynamic check (at runtime) using the JS
106    /// `instanceof` operator. This method returns `self instanceof T`.
107    ///
108    /// Note that `instanceof` does not always work with primitive values or
109    /// across different realms (e.g. iframes). If you're not sure whether you
110    /// specifically need only `instanceof` it's recommended to use `has_type`
111    /// instead.
112    fn is_instance_of<T>(&self) -> bool
113    where
114        T: JsCast,
115    {
116        T::instanceof(self.as_ref())
117    }
118
119    /// Performs a dynamic `instanceof` check to see whether the `JsValue`
120    /// provided is an instance of this type.
121    ///
122    /// This is intended to be an internal implementation detail, you likely
123    /// won't need to call this. It's generally called through the
124    /// `is_instance_of` method instead.
125    fn instanceof(val: &JsValue) -> bool;
126
127    /// Performs a dynamic check to see whether the `JsValue` provided
128    /// is a value of this type.
129    ///
130    /// Unlike `instanceof`, this can be specialised to use a custom check by
131    /// adding a `#[wasm_bindgen(is_type_of = callback)]` attribute to the
132    /// type import declaration.
133    ///
134    /// Other than that, this is intended to be an internal implementation
135    /// detail of `has_type` and you likely won't need to call this.
136    fn is_type_of(val: &JsValue) -> bool {
137        Self::instanceof(val)
138    }
139
140    /// Performs a zero-cost unchecked conversion from a `JsValue` into an
141    /// instance of `Self`
142    ///
143    /// This is intended to be an internal implementation detail, you likely
144    /// won't need to call this.
145    fn unchecked_from_js(val: JsValue) -> Self;
146
147    /// Performs a zero-cost unchecked conversion from a `&JsValue` into an
148    /// instance of `&Self`.
149    ///
150    /// Note the safety of this method, which basically means that `Self` must
151    /// be a newtype wrapper around `JsValue`.
152    ///
153    /// This is intended to be an internal implementation detail, you likely
154    /// won't need to call this.
155    fn unchecked_from_js_ref(val: &JsValue) -> &Self;
156}
157
158/// Trait implemented for wrappers around `JsValue`s generated by `#[wasm_bindgen]`.
159#[doc(hidden)]
160pub trait JsObject: JsCast + WasmDescribe {}