girgen
v0.3.13
Published
GIR Parser and type generator.
Readme
GirGen
GIR Parser and type generator.
Install
There are pre built binaries for linux-x64 and linux-arm64 published on NPM.
npm install girgen -D
./node_modules/.bin/girgen --helpOtherwise you can install from crates.io using cargo.
cargo install girgen --root .
./bin/girgen --helpTypeScript
Generate a standalone package that contains every namespace found in the given directories.
girgen typescript --help[!TIP]
You can use the Gnome flatpak SDK to acquire GIR files on systems that don't have them in one place, e.g NixOS or when you are targeting Flatpak.
flatpak run --command=cp --filesystem=home org.gnome.Sdk -r /usr/share/gir-1.0 gir-1.0 girgen -d gir-1.0 typescript
By default it will generate the package to .types/gi which you can then source
in tsconfig.json.
{
"compilerOptions": {
"lib": ["es2024"], // don't forget to specify a `lib` to avoid sourcing TypeScript's `dom` lib
"skipLibCheck": true, // it's recommended to turn this on
"typeRoots": [".types"]
}
}[!TIP]
Don't forget to gitignore generated files.
echo ".types/gi/" > .gitignore
Note that when using --alias flag to generate non version imports such as
gi://Gtk make sure to ignore the version you don't need so that it does not
end up as a union of the two versions.
girgen typescript -i Gtk-3.0 --aliasTypeScript Annotations
GObject has a few additional concepts about class methods and properties that cannot be expressed with TypeScript alone. For these girgen generates type only fields on classes and interfaces.
We have annotations for:
- signals
- readable properties
- writable properties
- construct-only properties
When implementing a GObject subclass you might want to annotate a subset of these or all of these depending on usecase.
Every class that inherits from GObject is going to include a namespace
containing type declarations where each member is written in kebab-case:
namespace MyClass {
export interface SignalSignatures extends GObject.Object.SignalSignatures {
// simple signal
"my-signal"(arg: number): void
// detailed signals are annotated with the `::{}` suffix
"my-detailed-signal::{}"(arg: number): void
}
// ReadableProperties is also used for notify signal annotations
export interface ReadableProperties
extends GObject.Object.ReadableProperties {
// property which has a public getter
"my-prop": number
}
export interface WritableProperties
extends GObject.Object.WritableProperties {
// property which has a public setter
"my-prop": number
}
export interface ConstructOnlyProperties
extends GObject.Object.ConstructOnlyProperties {
// property which can only be set at construction
"my-ctor-prop": number
}
}And the Class will refer to these using special $ prefixed fields:
[!IMPORTANT]
These fields don't exist at runtime, they are used by other APIs to introspect GObjects.
class MyClass extends GObject.Object {
declare readonly $signals: MyClass.SignalSignatures
declare readonly $readableProperties: MyClass.ReadableProperties
declare readonly $writableProperties: MyClass.WritableProperties
declare readonly $constructOnlyProperties: MyClass.ConstructOnlyProperties
static {
GObject.registerClass(
{
Signals: {
"my-signal": {
param_types: [GObject.TYPE_DOUBLE],
},
"my-detailed-signal": {
param_types: [GObject.TYPE_DOUBLE],
flags: GObject.SignalFlags.DETAILED,
},
},
Properties: {
"my-prop": GObject.ParamSpec.double(
"my-prop",
null,
null,
GObject.ParamFlags.READWRITE,
-GObject.Double.MAX_VALUE,
GObject.Double.MAX_VALUE,
),
"my-ctor-prop": GObject.ParamSpec.double(
"my-ctor-prop",
null,
null,
GObject.ParamFlags.CONSTRUCT_ONLY,
-GObject.Double.MAX_VALUE,
GObject.Double.MAX_VALUE,
),
},
},
MyClass,
)
}
// GObject.ConstructorProps can be used to infer props from the annotations
constructor(props: Partial<GObject.ConstructorProps<MyClass>>) {
super(props)
// note that properties will be annotated as camelCase
console.log(props.myProp, props.myCtorProp)
}
}Methods such as connect(), emit(), notify() will infer from these
annotations.
const instance = new MyClass()
instance.connect("my-signal", (source, arg) => {
console.log(arg)
})
instance.connect("my-detailed-signal::detail", (source, arg) => {
console.log(arg)
})
instance.connect("notify::my-prop", (_, pspec) => {
console.log(pspec.name)
})Due to how TypeScript this type works, you need to annotate this or use a
typecast to correctly infer types within the class.
class MyClass {
myFn(this: MyClass) {
this.emit("my-signal", 0)
}
myFn() {
const self = this as MyClass
self.emit("my-signal", 0)
}
}Module Augmentation
If you are using
Gio._promisify
you can augment namespaces.
import Gio from "gi://Gio?version=2.0"
import GLib from "gi://GLib?version=2.0"
Gio._promisify(
Gio.InputStream.prototype,
"read_bytes_async",
"read_bytes_finish",
)
declare module "gi://Gio?version=2.0" {
namespace GI {
namespace Gio {
interface InputStream {
read_bytes_async(
count: number,
io_priority: number,
cancellable: Gio.Cancellable | null,
): GLib.Bytes
}
}
}
}
declare const stream: Gio.InputStream
const bytes = await stream.read_bytes_async(4096, GLib.PRIORITY_DEFAULT, null)