29 releases
Uses new Rust 2024
| new 0.3.17 | May 15, 2026 |
|---|---|
| 0.3.16 | May 8, 2026 |
| 0.3.14 | Apr 14, 2026 |
| 0.3.7 | Mar 29, 2026 |
| 0.1.0 | Feb 8, 2026 |
#298 in Unix APIs
390KB
6.5K
SLoC
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 --help
Otherwise you can install from crates.io using cargo.
cargo install girgen --root .
./bin/girgen --help
TypeScript
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 --alias
TypeScript 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)
Dependencies
~7–11MB
~196K SLoC