Using WebXR types in Typescript

     

I’ve recently taken my hand made type definitions for WebXR (for use in my Mesh Game Engine) and submitted them to DefinitelyTyped (DT).

Lazily, I hadn’t switched the game engine over to using the DT version. The definitions just got their first update from a new PR so it seemed like the perfect time to swap over.

There were a number of gotchas in doing so, so I thought I’d post about how I got it working and some pitfalls you might run into if you’re into this kind of thing.

WebXR Polyfill

WebXR is supported by a lot of cool new devices - like the Oculus Quest. However, it doesn’t work on some older devices - like the original Oculus Rift. There is a polyfill that uses the “old” WebVR API to emulate the new WebXR API. There are several first generation headset that support the WebVR API. I have an older headset, so backward support is important to me.

If you want to support older devices install the polyfill and set it up as a runtime dependency:

"dependencies": {
  "webxr-polyfill": "^2.0.3"
},

Then, somewhere in your application, just call the polyfill to make it active. I have it in the main entry in my engine:

// @ts-ignore
import WebXRPolyfill from 'webxr-polyfill';
// @ts-ignore
try {
  new WebXRPolyfill();
} catch (e) {
  // shadup.
}

There may be a better way to do that.

WebXR Types

Install the WebXR types as a dev dependency (in package.json):

...
"devDependencies": {
  "@types/jest": "^23.3.14",
  "@types/webxr": "^0.1.0",
  ...
  "typescript": "^3.9.7"
},

Once you have the types installed it’s time to use them. Since these types do not have a library to install along side them (they are either built into the browser or handled by the polyfill at run time), when you import the types use the import type keyword:

...
import type {
    XRFrame,
    XRReferenceSpace,
    XRRenderStateInit,
    XRSession,
    XRSystem,
    ...
  } from 'webxr';
...

Note import type not simply import.

Stop Helping me Parcel

If you use Parcel, Parcel will try to auto install the library webxr when it sees your import. This package, unfortunately, exists but has nothing to do with the WebXR API (that I can tell).

So unless you want some random code running in your application that you didn’t ask for, you’ll need to stop Parcel from downloading and mucking up your code. Parcel says it has a flag --no-autoinstall, but in my experience Parcel ignores that flag and installs the package anyway.

The only way I could find to stop Parcel was to add the environment variable PARCEL_AUTOINSTALL=false:

...
  "start": "NODE_ENV=development PARCEL_AUTOINSTALL=false npx parcel index.html",
...

You’ll know Parcel decided to “help you”, if you see this magically appear in your package.json file:

...
"dependencies": {
  "webxr: 0.0.0",               // <---
  "webxr-polyfill": "^2.0.3"
},
...

This package is not needed, and, unless you want random code in your application, you should probably remove it from here, package-lock.json and node_modules.

Fin

When all that is setup, you should be ready to use the WebXR API in Typescript.

Using WebXR Types in a application