GObject decorators
Decorators that wrap GObject.registerClass.
Read more about GObjects in GJS on gjs.guide.
Required TypeScript settings
Make sure to enable experimentalDecorators and emitDecoratorMetadata, and to disable useDefineForClassFields.
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"useDefineForClassFields": false
}
}With target set to ES2022 or later, useDefineForClassFields defaults to true which shadows the getters and setters that @property installs on the prototype and field initializers bypass GObject entirely.
Example Usage
import GObject from "gi://GObject?version=2.0"
import { register, property, signal } from "gnim/gobject"
@register
class MyObj extends GObject.Object {
@property myProp: string = ""
@signal
mySignal(a: string, b: number): void {
// default handler
}
}What it (roughly) transpiles to
const priv = Symbol("private props")
class MyObj extends GObject.Object {
[priv] = { "my-prop": "" }
get myProp() {
return this[priv]["my-prop"]
}
set myProp(value) {
if (this[priv]["my-prop"] !== value) {
this[priv]["my-prop"] = value
this.notify("my-prop")
}
}
mySignal(a, b) {
return this.emit("my-signal", a, b)
}
on_my_signal(a, b) {
// default handler
}
}
GObject.registerClass(
{
Properties: {
"my-prop": GObject.ParamSpec.string(
"my-prop",
"",
"",
GObject.ParamFlags.READWRITE,
"",
),
},
Signals: {
"my-signal": {
param_types: [String.$gtype, Number.$gtype],
return_type: GObject.VoidType.$gtype,
},
},
},
MyObj,
)Property decorator
Property declarations can be used on fields and accessors:
class MyObject {
@property
field: number = 1
@property
get readonly(): number {}
@property
set writeonly(v: number) {}
@property
get readwrite(): number {}
set readwrite(v: number) {}
}IMPORTANT
When defining a setter you will have to explicitly emit the notify signal.
Property type declaration
The runtime type of the property will be inferred from TypeScript annotations. Optionally, it can be explicitly declared by passing an argument to the decorator.
type PropertyTypeDeclaration<T = unknown> =
| ((name: string, flags: ParamFlags) => ParamSpec<T>)
| ParamSpec<T>
| GType<T>
| { $gtype: GType<T> }The declaration can be
any class that has a registered
GType. This includes the globally availableString,Number,BooleanandObjectJavaScript constructors and any class that inherits fromGObject.Object.a function that produces a
ParamSpecwhere the passed name is a kebab cased version of the name of the property (for examplemyProp->my-prop), and flags is one of:ParamFlags.READABLE,ParamFlags.WRITABLE,ParamFlags.READWRITE.
class MyObject {
@property(GObject.UInt)
guint = 0
@property((name, flags) =>
GObject.ParamSpec.enum(
name,
null,
null,
flags,
Gtk.Orientation,
Gtk.Orientation.VERTICAL,
),
)
myOrientation = Gtk.Orientation.VERTICAL
}Omitting the explicit property declaration will use a default type guessed from the TypeScript annotation. Omitting both will result in a runtime error.
Property accessors
When implementing property setters you will also need to explicitly emit notify signals.
class MyObject {
#prop: number = 1
@property
set myProp(v: number) {
if (this.#prop !== v) {
this.#prop = v
this.notify("my-prop")
}
}
}Signal decorator
Signal decorator can be used on methods which will emit the signal:
class {
@signal
mySignal(arg: number): void {
// default handler
}
}Signal type declaration
The runtime type of the parameters and return type will be inferred from TypeScript annotations. Optionally they can be explicitly declared similarly to the property decorator.
class MyObject {
@signal([GObject.UInt], GObject.VoidType)
mySignal(arg: number): void {
// default handler
}
}SignalOptions
type SignalOptions = {
default?: boolean
flags?: SignalFlags
accumulator?: AccumulatorType
}Passing default: false to the signal will skip registering the method as a default handler.
class MyObject {
@signal({ default: false })
s() {
throw "this is never called"
}
@signal([], GObject.VoidType, {
default: false,
})
s() {
throw "this is never called"
}
}Register decorator
@register
class MyObj extends GObject.Object {}You can optionally pass the same options to this decorator as you would to GObject.registerClass.
@register({ GTypeName: "MyObj" })
class MyObj extends GObject.Object {}TIP
This decorator registers properties and signals defined with decorators, so make sure to use this and not GObject.registerClass.
Annotations
Signals and properties defined with decorators can be declared on the special $-prefixed type fields with the Annotations type helper, which makes them inferable by methods such as connect() and notify().
import { register, property, signal, type Annotations } from "gnim/gobject"
@register
class MyObj extends GObject.Object {
declare readonly $signals: GObject.Object.SignalSignatures &
Annotations<MyObj, "mySignal">
declare readonly $readableProperties: GObject.Object.ReadableProperties &
Annotations<MyObj, "myProp">
@property myProp: string = ""
@signal
mySignal(arg: string): void {}
}See Type annotations for details.