Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

JsInterop consuming JS with a attributes

105 views
Skip to first unread message

Craig Mitchell

unread,
Jan 13, 2025, 12:02:40 AMJan 13
to GWT Users
I'm calling some existing JS that returns a JS Object which I've implemented in JsInterop:

@JsType(isNative = true, namespace = "window.CrazyGames.SDK")
public static class JsUser {
    public native Promise<CrazyGamesUser> getUser();
}

I can happily call it:
sdk.user.getUser()
  .then(user -> {
    // Do something with the user
    return null;
  })
  .catch_(error -> {
    return null;
  });

This issue is I'm struggling to work out how to define the return object "CrazyGamesUser".  The actual JS object is just this:
{
    "username": "SingingCheese.TLNU",
    "profilePictureUrl": "https://images.crazygames.com/userportal/avatars/4.png"
}

If I define it like this:

@JsType(isNative = true, namespace = JsPackage.GLOBAL)
public static class CrazyGamesUser {
  public String username;
  public String profilePictureUrl;
}

I get a java.lang.ClassCastException.

So if I set the name to "?":

@JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL)
public static class CrazyGamesUser {
  public String username;
  public String profilePictureUrl;
}

Then I get a compile error:
'?' can only be used as a name for native interfaces in the global namespace.

But if I make it an interface, I can't have the member variables.

If I do remove the member variables, it does work, and I can access them via some JSNI:

public static native String getUsername(CrazyGamesUser instance) /*-{
  return instance.username;
}-*/;

But that's really ugly.  What's the correct approach here?

Thanks.

Craig Mitchell

unread,
Jan 13, 2025, 1:47:28 AMJan 13
to GWT Users
Overlay types do work, and make it a little better:

public class CrazyGamesUser extends JavaScriptObject {
  protected CrazyGamesUser() {}
  public final native String getUserName() /*-{ return this.username; }-*/;
  public final native String getProfilePictureUrl() /*-{ return this.profilePictureUrl; }-*/;
}

However, it would be great if JsInterop just worked, so I could just do:

@JsType(isNative = true, namespace = JsPackage.GLOBAL)
public static class CrazyGamesUser {
  public String username;
  public String profilePictureUrl;
}

Maybe I'm missing something?

Thomas Broyer

unread,
Jan 13, 2025, 3:03:43 AMJan 13
to GWT Users
Use an interface rather than a class.


Rule of thumb is: use a class only if it maps to a "constructor" in JS, i.e. something you'll either create an instance of, or use in an "instanceof" check; for everything else (generally something you get from another API or "receive" in a callback), use an interface.

In this case, I think you could also use name="Object".

Craig Mitchell

unread,
Jan 13, 2025, 4:14:33 AMJan 13
to GWT Users
Hi Thomas,

Can an interface have attributes?  I thought interfaces can only have methods?

So if I make it:

@JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL)
public interface CrazyGamesUser {
}

I can't see how I can add the attributes:

public String username;
public String profilePictureUrl;

?

Thanks again.

Craig Mitchell

unread,
Jan 13, 2025, 4:21:27 AMJan 13
to GWT Users
Oh, wait.  Leaving it as a class, and making the name "Object" worked!

@JsType(isNative = true, name = "Object", namespace = JsPackage.GLOBAL)
public class CrazyGamesUser {

  public String username;
  public String profilePictureUrl;
}

Woohoo!  Thanks Thomas!

That probably should be documented somewhere (apologies if it is, I thought I read through all the doco).

Thomas Broyer

unread,
Jan 13, 2025, 12:19:36 PMJan 13
to GWT Users
On Monday, January 13, 2025 at 10:14:33 AM UTC+1 ma...@craig-mitchell.com wrote:
Hi Thomas,

Can an interface have attributes?  I thought interfaces can only have methods?

Yes, you'd have to make them getters (probably need to annotate them with @JsProperty too)
Message has been deleted

Craig Mitchell

unread,
Jan 13, 2025, 5:02:07 PMJan 13
to GWT Users
Thanks again Thomas,

Yep, getters worked.  I thought I tried this previously, but obviously not.

I didn't need the JsProperty annotation. maybe because the interface is marked as native:

@JsType(isNative = true, name = "?", namespace = JsPackage.GLOBAL)
public interface CrazyGamesUser {
  String getUsername();
  String getProfilePictureUrl();
}

I'll update https://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJsInterop.html to mention this is how you access attributes using JsInterop.

Cheers.

Jens

unread,
Jan 13, 2025, 5:39:28 PMJan 13
to GWT Users
Interesting. As far as I know it should not have worked. Your above example should map to JavaScript:

{ getUsername: function() { .... }, getProfilePictureUrl: function() { .... } }

At the bottom of the jsinterop page there is a link to the JsInterop spec: https://docs.google.com/document/d/10fmlEYIHcyead_4R1S5wKGs1t2I7Fnp_PaNaa7XTEk0/edit

It states that you need @JsProperty in that case. GWT compiler must know if it should treat the interface method as method or as property.

-- J.

Craig Mitchell

unread,
Jan 13, 2025, 10:57:22 PMJan 13
to GWT Users
In the Native JsTypes section here, they have the example:

@JsType(isNative = true)

public interface Foo {

  int getFoo();

}


Which doesn't have the JsProperty annotation.  Oddly, it then tells you to read the section "Property setters & getters" which tells you that you should put it on.

I'm going to put it on.  I've no clue how it works without it.
Reply all
Reply to author
Forward
0 new messages