In my Three.js debugging workflow, I often attach objects to globalThis
for convenience. However, manually declaring types for each property is becoming tedious as the globalThis object grows larger.
I want to know if TypeScript can automatically infer types for keys that end with a specific string, such as Material
.
For example, I would like all properties on globalThis
that end with Material (i.e., keys matching the template string type ${string}Material
) to be treated as THREE.MeshPhysicalMaterial.
declare global {
var camera: THREE.PerspectiveCamera
var scene: THREE.Scene
var renderer: THREE.WebGLRenderer
var setCameraToLeft: () => void
var setCameraToRight: () => void
var bigBoxMaterial: THREE.MeshPhysicalMaterial
var middleBoxMaterial: THREE.MeshPhysicalMaterial
var smallBoxMaterial: THREE.MeshPhysicalMaterial
...Material: THREE.MeshPhysicalMaterial
}
Perhaps it is a definition similar to this...
declare global {
var camera: THREE.PerspectiveCamera
var scene: THREE.Scene
var renderer: THREE.WebGLRenderer
var setCameraToLeft: () => void
var setCameraToRight: () => void
[key: string]: key extends `${string}Material` ? THREE.MeshPhysicalMaterial : unknown
}
Looks like you want to merge an index signature into typeof globalThis
. Unfortunately that's not possible; there's a feature request at microsoft/TypeScript#57353 for that, but it doesn't have much engagement, so I doubt we'll see this soon.
The closest I can imagine getting is to merge an index signature into an interface that corresponds to the global object, such as the Window
interface in the DOM:
declare global {
interface Window {
[k: `${string}Material`]: THREE.MeshPhysicalMaterial
}
}
That's using a template string pattern index signature to say that every key ending in Material
will have a property of type THREE.MeshPhysicalMaterial
(which isn't likely to be true, right? Maybe you want | undefined
in there or maybe you want to use --noUncheckedIndexedAccess
)
Then you can index into window
(but not into globalThis
or use a global variable) to access things:
window.randomMaterial.alphaHash; // okay
You could also possibly redeclare globalThis
like
declare let globalThis: Window
and then globalThis.randomMaterial.alphaHash
works, but this is getting even more fragile.
So you can get somewhat closer to your goal than just giving up, but I don't know that it's worth the effort. In some sense it's no better than just using a type assertion on a variable that references globalThis
, and not trying to actually mess with merging at all:
const myGlobal = globalThis as typeof globalThis & {
[k: `${string}Material`]: THREE.MeshPhysicalMaterial
};
myGlobal.randomMaterial.alphaHash; // okay