# gjs-gtk-typescript **Repository Path**: mirrors_codejamninja/gjs-gtk-typescript ## Basic Information - **Project Name**: gjs-gtk-typescript - **Description**: gtk typescript app proof of concept - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-08-08 - **Last Updated**: 2026-09-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # gjs-gtk-typescript > gtk typescript app proof of concept ![](assets/gjs-gtk-typescript.png) A minimal GTK application written in TypeScript and run on [GJS](https://gjs.guide/), the GNOME JavaScript runtime. One window, one button, real types. ## Status (2026) **This is a proof of concept, kept as a reference. It is not a framework and it is not going to become one.** When this was written in 2019 there was no good way to do any of it. GJS had no ES module support, so the build ran TypeScript through Babel and used `babel-plugin-gjs` to rewrite `import` statements into `imports.gi.*` lookups. There were no real GTK type definitions, so `src/@types/gjs.d.ts` declared `Gtk: any` and the "TypeScript app" was mostly untyped. It was launched with [`cgjs`](https://github.com/cgjs/cgjs), a Node-compatibility layer for GJS. None of that is necessary now, and the 2026 revive deletes all of it: - **GJS speaks ESM natively** since 1.70, so `import Gtk from 'gi://Gtk?version=4.0'` just works. No Babel, no plugin, no `cgjs`. TypeScript compiles straight to ESM and `gjs -m` runs the output. - **Real types come from [`ts-for-gir`](https://github.com/gjsify/ts-for-gir)** via the published `@girs/*` packages, generated from the system GIR data. `Gtk.Button` is genuinely `Gtk.Button` now, not `any`. If you want to build a GNOME app in TypeScript today, start from [gjs.guide](https://gjs.guide/), take types from `@girs/*`, and look at [`react-gtk`](https://gitlab.com/bitspur/react-gnome/react-gtk) if you want React on top. This repository is the smallest possible illustration of the underlying setup, nothing more. The app was also ported from GTK 3 to GTK 4, since GTK 3 is in maintenance and `Gtk.init` / `Gtk.main` / `show_all` no longer exist. See the [changelog](CHANGELOG.md). ## Dependencies - [GJS](https://gjs.guide/) 1.70 or newer — 1.88 on macOS via Homebrew, 1.82 on Debian trixie - [GTK 4](https://www.gtk.org) - Node 20.19+ and pnpm, for the TypeScript build only ```sh brew install gjs gtk4 # macOS apt install gjs gir1.2-gtk-4.0 libgtk-4-1 # Debian / Ubuntu ``` ## Usage ```sh make prepare # asdf toolchain, gjs, gtk4, pnpm install make start # build and open the window ``` Or, without make: ```sh pnpm install && pnpm build && pnpm start ``` Clicking the button prints `Hello World`. ## How it works ``` src/window.ts builds the Gtk.ApplicationWindow and its button src/application.ts creates the Gtk.Application and wires `activate` src/index.ts entry point; handles `--smoke` and runs the application ``` GTK 4 has no `Gtk.init` / `Gtk.main` pair. A `Gtk.Application` owns the main loop and calls back on `activate` once the toolkit is ready, so the whole app is three small modules with no global setup. `src/index.ts` accepts a `--smoke` flag, which lets the window realise for one main-loop turn and then quits. That is what makes the app verifiable on a machine nobody is sitting at. ## Testing `tests/smoke.js` runs under GJS against the built `lib/` and asserts, in TAP format, that the application activates, presents a window, that the window's child is a button with the expected label, and that clicking it runs the handler. ```sh make test # on this machine, needs gjs + gtk4 + a display make test/e2e # in a Debian container under Xvfb, needs only docker ``` There is no Node-side unit tier, and none is stubbed: every import in this project is a `gi://` module that only the GJS runtime can resolve, so tests have to run under GJS. `make test` emits `coverage/coverage.lcov` using GJS's built-in instrumentation, plus a line-coverage summary on stdout. That coverage is measured against the emitted `lib/*.js` rather than `src/*.ts` — GJS instruments the files it actually loads and there is no source-map remapper in this toolchain — which is why `sonar-project.properties` deliberately does not declare a coverage path. `make test` skips itself with a TAP `# SKIP` line when no display is available, so it is safe to run over ssh. ### macOS note Homebrew's `gjs` bottle ships typelibs that reference `libgjs.0.dylib` by bare name, which dyld cannot resolve, so `gjs -m` fails with `No property 'Gtk' in GI repository object`. The fix is `DYLD_FALLBACK_LIBRARY_PATH="$(brew --prefix)/lib"`, which `make` and `bin/gjs-gtk-typescript` both apply for you. It has to be set inline on the gjs command rather than exported, because macOS SIP strips `DYLD_*` from the environment whenever a protected binary such as `/bin/sh` execs. ## Development ```sh make build # tsc -> lib/ make typecheck # tsc --noEmit make lint # oxfmt --check, oxlint, tsc --noEmit make format # oxfmt make count # cloc ``` ## License [MIT License](LICENSE) [Clay Risser](https://clayrisser.com) © 2019 ## Related projects - [gjs.guide](https://gjs.guide/) — the official GJS documentation - [ts-for-gir](https://github.com/gjsify/ts-for-gir) — generates the `@girs/*` types this uses - [react-gtk](https://gitlab.com/bitspur/react-gnome/react-gtk) — React renderer for GTK, also Bitspur - [ts-gir](https://gitlab.com/bitspur/misc/ts-gir) — the 2019 type generator this project predates the need for