gjsify / ts-for-gir

TypeScript type definition generator for GObject introspection interfaces
https://gjsify.org/pages/projects
Apache License 2.0
241 stars 18 forks source link

Interfaces #13

Closed realh closed 2 years ago

realh commented 5 years ago

GObject interfaces being exported as TS classes instead of interfaces can be an inconvenience, requiring one to override type safety with explicit casts.

For usability it would be best if the TS definition referred to a TS interface, ie simply change class to interface. However, that would hide the JS object we need to use to initialise classes that implement GInterfaces. But they could still be accessed by looking up the names as strings in imports.gi.*. When a new decorator-based API is designed to replace GObject.registerClass there's an opportunity to make this a non-issue by making the g_implements (or whatever it ends up being called) decorator accept a string as an alternative to a class.

JumpLink commented 2 years ago

A decorator based API would be very nice, but I think it will be a long time before ECMAScript decorators are included in the standard.

Conveniently, we are in the TypeScript world where decorators have been used for a long time. So I would like to develop a TypeScript framework where we could implement decorators for GObject classes ourselves. Then it would probably not be a problem anymore if GObject interfaces are also generated as Typescript interfaces.

So the question is, do we want to implement that here or in a separate framework and do we want to continue to support interfaces as classes in ts-for-gir then?

realh commented 2 years ago

I've written my own decorators. They would need some work to make them suitable for "production", but they might make a useful starting point.

gdeco.js gdeco.d.ts

I think the gjs developers are open to improving the API for class registration etc in a way that's forward compatible with decorators, so you could try liaising with them.

realh commented 2 years ago

On the subject of interfaces as classes, I found that when you define a class in Typescript, it automatically creates an interface with the same name, so you might as well continue to treat GObject interfaces as TS classes, making it easier to support static methods etc. For my inheritance fork, there is effectively no distinction between GObject classes and interfaces. Each one is declared both as a TS interface containing the instance methods and properties, and a TS object (not a class) containing the static methods etc. This almost completely solves the problem with clashing overloads, but there are a few cases that need workarounds.

JumpLink commented 2 years ago

Thanks a lot, I think this information will help me a lot to be able to implement the whole thing, I will also take a closer look at your fork as soon as I tackle this.

Submitting the decorators directly to Gjs is a good idea, but I will have to experiment with it myself beforehand to provide a clean implementation.

I find the following project very interesting, this provides the Typescript types at runtime and builds a framework on top of it which uses the types e.g. for dependency injection. I would like to experiment with this, it might be cool to combine registering GObject classes with decorators and dependency injection.

But I'm not sure yet if that might not be too much, what do you think?

realh commented 2 years ago

I just did a bit of maintenance on my fork, because I thought it would be a good idea to use the @deprecated jsdoc tag on the false method definitions it adds to deal with overload clashes. I can't test it though, because the generate command now fails with this error:

  type: 'core',
  root: '/Users/tony/Code/ts-for-gjs',
  name: '@oclif/plugin-help'
}
module: @oclif/config@1.18.3
task: loadPlugins
plugin: ts-for-gir
root: /Users/tony/Code/ts-for-gjs
See more details with DEBUG=*
(Use `node --trace-warnings ...` to show where the warning was created)
CLIError: No module found!
    at Object.error (/Users/tony/Code/ts-for-gjs/node_modules/@oclif/errors/lib/index.js:26:15)
    at Generate.error (/Users/tony/Code/ts-for-gjs/node_modules/@oclif/command/lib/command.js:60:23)
    at Generate.<anonymous> (/Users/tony/Code/ts-for-gjs/src/commands/generate.ts:84:26)
    at Generator.next (<anonymous>)
    at fulfilled (/Users/tony/Code/ts-for-gjs/src/commands/generate.ts:8:58) {
  oclif: { exit: 2 },
  code: undefined
}
 ›   Error: No module found!

I hope that means something to you. TBH I'm finding all this node/npm infrastructure a bit of a barrier.

I've only skim-read that DeepKit article, but it seems it's based on a new Typescript compiler that embeds the new type information in bytecode. Presumably this bytecode doesn't replace JS, but supplements it, and it generates JS too with wrappers that interact with the bytecode? Or are they talking about a completely new runtime that isn't compatible with current browsers or node? Anyway, it does seem like overkill for gjs. I think gjs was only ever intended for scripting and extending gnome-shell (some extensions can get quite complex though), and unless that bug with callbacks during GC gets fixed, it isn't really usable for much more than that.

JumpLink commented 2 years ago

You can ignore the stack trace, decisive is the message "No module found", which means that no gir files were found that you are looking for. So maybe you need to set --girDirectories. You can use the list command to see what modules are found without triggering a new generation.

I noticed the stack trace in your fork too, but it has no negative effects, I can still work with it.

Btw. In the current version I have replaced @oclif with yargs since yargs also supports Deno and ESM and is less confusing

realh commented 2 years ago

Ah, OK, that'll be because I'm trying to run it on a Mac at the moment. I installed the g* packages with homebrew, which uses /usr/local on Intel (unless they changed that too, but only for fresh installs so it doesn't break existing setups), but switched to /opt/homebrew for the M1 version. IIRC brew's gobject-introspection package used to return an incorrect value for pkg-config --variable=girdir gobject-introspection-1.0 but that seems to be fixed now. It would be nice if meson's gnome plugin could use pkg-config to choose default install locations for that sort of thing, but I'm not sure that allowing installation outside of ${prefix} is such a good idea.

JumpLink commented 2 years ago

Okay now inheritance and implementations are merged to master :)

JumpLink commented 2 years ago

@realh I think this issue is solved now :)