Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getter and Setter types #2869

Open
gluax opened this issue Apr 20, 2022 · 2 comments
Open

Getter and Setter types #2869

gluax opened this issue Apr 20, 2022 · 2 comments
Labels

Comments

@gluax
Copy link

gluax commented Apr 20, 2022

Hi all we are trying to set proper types on getter and setters rather than it just showing as any from typescript on the field test3.

However, we can find very little documentation on how this crate actually works despite the guidebook.

The code is as follows:

use indexmap::IndexMap;
use serde_json::Value;
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};

#[wasm_bindgen(typescript_custom_section)]
const METADATA_TYPE: &'static str = r#"
export type JSMetadata = {
    [key in string]: any
}
"#;

#[wasm_bindgen(typescript_type = "JSMetadata")]
struct Metadata;

pub trait ToJS: Sized {
    type Error;

    fn to_js(&self) -> Result<JsValue, Self::Error>;
}

impl ToJS for IndexMap<String, Value> {
    type Error = Box<dyn std::error::Error>;

    fn to_js(&self) -> Result<JsValue, Self::Error> {
        Ok(JsValue::from_serde(self)?)
    }
}

#[wasm_bindgen]
pub struct Tester {
    #[wasm_bindgen(skip)]
    pub test1: String,
    pub test2: u32,
    #[wasm_bindgen(skip, typescript_type = "JSMetadata")]
    pub test3: IndexMap<String, Value>,
}

#[wasm_bindgen]
impl Tester {
    #[wasm_bindgen(constructor)]
    pub fn new(a: &str) -> Self {
        Self {
            test2: 10,
            test3: {
                let mut aaa = IndexMap::new();
                aaa.insert(a.to_string(), a.into());
                aaa
            },
            test1: a.to_string(),
        }
    }

    #[wasm_bindgen(getter)]
    pub fn test1(&self) -> String {
        self.test1.clone()
    }

    #[wasm_bindgen(setter)]
    pub fn set_test1(&mut self, field: String) {
        self.test1 = field;
    }

    #[wasm_bindgen(getter)]
    pub fn test3(&self) -> JsValue {
        self.test3
            .to_js()
            .expect("Failed to convert field to JSMetadata")
    }

    #[wasm_bindgen(setter)]
    pub fn set_test3(&mut self, field: JsValue) {
        self.test3 = field
            .into_serde()
            .expect("JsValue was not of type {  [key in string]: any }.")
    }
}

How do we specify that for the field test3 it should be of type JSMetadata as we defined above?

@gluax gluax added the question label Apr 20, 2022
@trevyn
Copy link
Contributor

trevyn commented Apr 20, 2022

Is there a reason you have #[wasm_bindgen(skip)] on the test3 field? It seems like this might be causing your problem, and you don't need this even if you override both the getter and the setter, see the example here: https://rustwasm.github.io/docs/wasm-bindgen/reference/attributes/on-rust-exports/getter-and-setter.html

@gluax
Copy link
Author

gluax commented Apr 20, 2022

@trevyn I do have to skip since the type there since IndexMap, nor do HashMap, support the into_wasm_abi traits. Nor does the Value type from serde.

We managed to find a way around it by doing the following(though it still feels like this process could be improved):

use indexmap::IndexMap;
use serde_json::Value;
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};

#[wasm_bindgen(typescript_custom_section)]
const METADATA_TYPE: &'static str = r#"
export type JSMetadata = {
    [key in string]: any
};
"#;

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(typescript_type = "JSMetadata")]
    pub type Test3;
}

#[wasm_bindgen(getter_with_clone)]
pub struct Tester {
    pub test1: String,
    pub test2: u32,
    #[wasm_bindgen(skip, typescript_type = "JSMetadata")]
    pub test3: IndexMap<String, Value>,
}

#[wasm_bindgen]
impl Tester {
    #[wasm_bindgen(catch, constructor, js_class = "Tester")]
    pub fn new(test1: String, test2: u32, test3: Option<Test3>) -> Self {
        Self {
            test1,
            test2,
            test3: if let Some(t) = test3 {
                t.into_serde().unwrap()
            } else {
                Default::default()
            },
        }
    }

    #[wasm_bindgen(getter = test3, typescript_type = "JSMetadata")]
    pub fn test3(&self) -> JsValue {
        JsValue::from_serde(&self.test3).unwrap()
    }

    #[wasm_bindgen(setter = test3, typescript_type = "JSMetadata")]
    pub fn set_test3(&mut self, field: Test3) {
        self.test3 = field.into_serde().unwrap()
    }
}

Not sure if doing typescript_type on all of those instances of wasm_bindgen is necessary though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants