Re: A Typescript experiment





On 11 June 2017 at 00:04, <philip chimento gmail com> wrote:
On Fri, Jun 9, 2017 at 2:29 PM Sam Jansen <sam jansen starleaf com> wrote:
Hello,

I've been following in the steps of some others [1] [2] who have attempted to produce Typescript definitions for Gtk and Gjs. I believe I have something more complete than any other effort I've found, so I thought it was time to share my work, and a couple of questions.

Starting with gir2dts [2], I tried to write a basic application... And failed. Due to various limitations (no constructors, interfaces, etc.) So I went about at fixing any limitation I found and have ended up with a fairly thorough description in Typescript of the GI interfaces presented by Gjs.

Examples of some features I'm particularly happy with:
  • Signals, via the connect() function, are checked using Typescript's string literal type checking [3]
  • The above includes the "notify::attribute" syntax
  • All possible parameters for a class are described in an interface, and this is used in the default Gjs constructor. This allows editors like Visual Studio Code to provide code completion and type checking for the arguments to this object
  • Classes are decomposed into static and instance sides. The static side is used to describe the Gjs constructor, and then extended with the other GI constructor functions
  • Ability to include the documentation, such that it will be shown in code completion popups
  • Multiple return values described correctly

All of this is possible due to the excellent work of the original project, gir2dts, that I have simply forked and added extra support to.

This project is available at: https://github.com/sammydre/gir2dts

This is *fantastic*.
 

Thanks :)
 
I'm still not sure it's all that useful generally just yet -- largely due to me not knowing how such code should be distributed, or work with module systems. Any comments on how to structure this would be appreciated.

I admit I don't know much about Typescript. If I understand correctly, this would be a plugin for the Typescript compiler, so it should be distributed in whatever way is usual there.
 

It seems the way most people are using it now is via "@typings", which is supported by npm.

I think the best thing for me to do is document what I have at the moment / write a good example and point to it here to solicit further discussion. So I'll come back to this at some point in the future, all going well.
 
On to my questions:
  • In some cases functions and properties end up with the same name in _javascript_. Take the "is_active" [4] function and "is-active" [5] property, for example. How does Gjs decide how to resolve ".is_active"? My research suggests it favours the property, with no ability to call the function on the object. Is this correct?
Your statement is correct, but defining GObject properties that conflict with methods is considered a bug in the libraries themselves. We do have patches for GJS that will give priority to the methods, but they have not as yet been committed because it's a minor API break [6]. 

Ah, good to know. 
  • Similarly, if a class has a method called "connect()", is it impossible to call, due to the signal-connection function being the only one available? This is the case in e.g. Gio.Socket.
This is considered a bug in GJS [7], up for grabs if you want to work on a patch; but we should preferably be able to do it without breaking API.

That's interesting. Perhaps something for me to come back to in the future.
 
  • I tried to use "GLib.idle_add(func, data)" -- the GObject introspection suggests this is the correct signature. Unfortunately it appears it needs a priority argument first, but I don't understand why this is. Could you explain what I'm missing to correctly generate the typing for this function?
In the GIR file, g_idle_add_full() is marked as "shadowing" g_idle_add(), replacing the original g_idle_add() in bindings. Your generator should take this into account and use the signature for g_idle_add_full(). (Here's the relevant GIR snippets:

<function name="idle_add" c:identifier="g_idle_add" shadowed-by="idle_add_full" introspectable="0">
<function name="idle_add_full" c:identifier="g_idle_add_full" shadows="idle_add">

(In particular, don't generate a signature for symbols marked introspectable="0", since they won't exist in GJS.)

Thanks, that's helpful. It showed off two bugs in my implementation: I didn't obey "introspectable" for top-level functions (I'd only implemented it for class methods), and that I had no support for the "shadowing" attribute. I've now fixed the former, and I'll come back to the latter and implement it before too long.

  • I've sometimes found that derived functions have different signatures, with Typescript does not like. An example of this is the "activate" symbol used in Gtk; consider "Widget.activate()" vs. "Button.activate(button: Button)" -- Button derives from Widget, but has a different signature for this method. What does Gjs do about this? "let a = new Gtk.Button({}); a.activate()"
There is no Gtk.Button.activate() method. I guess you are probably referring to the virtual method, which is called vfunc_activate() in GJS. Virtual methods are not called directly from JS code, instead you would add a vfunc_activate() method to a subclass of Gtk.Button if you wanted your subclass to have different or additional behaviour from the default Gtk.Button. (And the button parameter is lopped off, since it's bound to "this" inside the method.) This method would only be called internally by GTK.

If you execute new Gtk.Button().activate() it calls gtk_widget_activate().

The documentation in our DevDocs instance might shed some light here [8]; as far as I know it generates all the method signatures correctly, and you can feel free to borrow from that code.

I chose a bad example here. It showed that I had completely misunderstood the GI "virtual functions" and their mapping to GJS. Based on your input, I've now fixed that.

But my question still stands, albeit with slightly different classes. Consider:

MenuItem derives from Widget (via Bin via Container).

The activate() method has a different signature. For widget, it returns a boolean. For MenuItem, it returns void.

What does GJS do in this case? Will calling activate() always result in calling the MenuItem version on a MenuItem instance, making it impossible to call the Widget version directly? Or something else?

BTW, the devdocs is very useful - I didn't know that existed before your email!

Cheers,
Sam
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]