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.
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.
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?
- 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.
- 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?
- 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()"
Finally, a few examples of this at work:
The start of the Gtk.Button interface:
export interface Button extends Bin {
clicked():void
enter():void
get_alignment():[/*xalign:*/ number, /*yalign:*/ number]
Signals are described explicitly, both property change and otherwise:
connect(sigName: "notify::xalign", callback: ((obj_: Button, pspec: GObject.ParamSpec) => void))
connect(sigName: "notify::yalign", callback: ((obj_: Button, pspec: GObject.ParamSpec) => void))
connect(sigName: "activate", callback: ((button_: Button) => void)): void
connect(sigName: "clicked", callback: ((button_: Button) => void)): void
Parameters possible on construction:
interface Button_Parameters extends Bin_Parameters {
always_show_image?: boolean;
focus_on_click?: boolean;
image?: Widget;
image_position?: PositionType;
label?: string;
relief?: ReliefStyle;
use_stock?: boolean;
use_underline?: boolean;
xalign?: number;
yalign?: number;
}
The constructors use interface merging to allow both a constructor function and a function called new:
export interface Button_Constructor {
new (config: Button_Parameters): Button
}
export declare class Button_Constructor {
new(): Button
new_from_icon_name(icon_name: string, size: number): Button
new_from_stock(stock_id: string): Button
new_with_label(label: string): Button
new_with_mnemonic(label: string): Button
}
export declare var Button: Button_Constructor
Cheers,
Sam