#Skip to menu

My experience developing a PWA with TypeScript


I wanted to learn TypeScript and decided to create some little games. The games are developed with Phaser which comes with TypeScript definitions, and I must say that the autocompletion and jump to definition was a big help while learning the API. I’m using the tide Emacs mode to develop, which works amazingly, except for two annoying problems:

  1. When first configuring tsconfig.json I copied it from another part and it had invalid JSON. Things weren’t working as expected, but tide reported no error. I had the same problem one month later when after modifying tsconfig I added a comma on the last item of an array (which is invalid JSON).
  2. Tide highlights errors directly inside the buffer, but only after saving the buffer. I configured Emacs to save the buffer when the frame loses focus so I have to constantly move the mouse to another window, and back, which makes me lose focus, too.

Inside Emacs I remapped the F3 key, and right click, to jump to a definition and ESC to jump back, which makes navigation seamless. F2 “autofixes” (calling tide-fix) an error reported by the TypeScript compiler, while F4 renames an identifier. Like so:

(defun setup-tide-mode ()
  (flycheck-mode +1)
  (tide-hl-identifier-mode +1)
  (company-mode +1)
  (define-key tide-mode-map [mouse-3] (lambda (n) (interactive "e") (mouse-set-point n) (tide-jump-to-definition)))

  (define-key tide-mode-map [f3] (lambda (n) (interactive "e") (mouse-set-point n) (tide-jump-to-definition)))

  (define-key tide-mode-map [f2] 'tide-fix)
  (define-key tide-mode-map [f4] 'tide-rename-symbol)
  (define-key tide-mode-map [escape] 'pop-tag-mark)
  (setq tide-completion-ignore-case 't)
  (setq tide-completion-detailed 't)

Initially I wanted to develop an Android app, but I opted for the web for two reasons:

  1. I liked the idea of PWAs which work both on a browser and can be installed on user’s device, all while taking advantage of all the features of modern browsers.
  2. I didn’t want to lock-in in a single ecosystem.
  3. I wanted to learn something that I could reuse easily in the future for my projects.

I like the idea of PWAs, even though it adds yet another annoying popup to websites… Another problem is that they still lack many features that would be available with native apps.

My games also vibrate, and to avoid annoying visitors (with a popup) they vibrate only when the APP is installed on user’s device. Unfortunately there’s no standard way yet to detect if the PWA is installed, but I found a way to detect it on Chrome and iOS.

To make the games work offline a service worker is needed, which when “installed” fetches all the needed resources and later intercepts a fetch of a resource and returns it from the cache. Unless you do it from the console there doesn’t seem to be a way to refresh the cache of a service worker, so cache versioning must be used when assets are changed. Right now I change the version manually, even though a smarter way would save user’s bandwidth.

I had some problems making tsc detect that service_worker.js contains a service worker. I found this workaround which adds the following two lines at the beginning of the file:

export default null
declare var self: ServiceWorkerGlobalScope;

Inside the Web Manifest you can decide if the installed PWA must start in fullscreen mode. Unfortunately I couldn’t find a way to make an element go fullscreen without the user interacting with the page.

I wanted to add my games to https://pwa.rocks/ but their GitHub seems inactive, so I abandoned the idea.

To make the PWA installable, some rules must be followed. Chromium has a useful tool called Lighthouse that let’s you know if anything is wrong with your PWA, but unfortunately it doesn’t work on Debian for now (see this issue). Anyway, I found online some sites that let you run Lighthouse.

Initially I wanted to write games2d using Rust and compiling to WASM, but at the time the ecosystem was too immature for my taste. I’ll give it a try later on, for sure!