What is the correct way to extend a namespace/module with babylon in TS?

Probably more of a TS question than Babylon.
Specifically I am following the example: https://www.babylonjs-playground.com/#HH1U5#87

In there, they have extended the ArcRotateCamera by BABYLON.ArcRotateCamera.prototype.spinTo= ...

but I couldn’t get it working with TS, here is my code:

import * as BABYLON from "babylonjs";


// this also doesn't work
//declare namespace BABYLON {
declare module "babylonjs" {
  export interface UniversalCamera {
    moveTo: (...args: any[]) => any,
  }
}

// extend babylon UniversalCamera
BABYLON.UniversalCamera.prototype.moveTo = function (whichprop, targetval, speed) {
  const ease = new BABYLON.CubicEase();
  ease.setEasingMode(BABYLON.EasingFunction.EASINGMODE_EASEINOUT);

  BABYLON.Animation.CreateAndStartAnimation(
    "UniversalCamera-moveTo",
    this, whichprop,
    speed, 120,
    this[whichprop], targetval,
    0, ease
  );
};

(P.S. TS is sooooo annoying, I feel like half of my time is spent on pleasing the compiler than actually building stuff, would appreciate if there is a better workflow, e.g. is JS + JSDoc a potential better solution?)

Your best bet would probably be to write a new class that extends UniversalCamera, and adds this additional method moveTo. Then you can just use that class as a type.

1 Like

Hi, thanks for the help. So extending is not an option?
Do you have some idea why the interface merging feature of TS is not working here?

Would prefer to extend it because otherwise I have to assert/cast the camera everytime I do stuff like scene.getCameraByName() etc :frowning:

for the record, I ended up implemented it as helper function which take camera as one of the params.

would still like to know why TS extends is not working here though

1 Like

Hmm…not sure. This is really more of a TypeScript question than a Babylon question. Maybe @RaananW would know

well on first look it does look like it should work, but in typescript it’s sometimes the little nuances :slight_smile:

This is happening because of the way we export out declarations. This will work:

import { UniversalCamera } from "babylonjs";


declare module "babylonjs/Cameras/universalCamera" {
    export interface UniversalCamera {
        moveTo: (...args: any[]) => any;
    }
}

UniversalCamera.prototype.moveTo = (...args: any[]) => {
    console.log(args);
};

And this will work as well:

import { UniversalCamera } from "babylonjs";

declare module "babylonjs" {
    interface TouchCamera {
        moveTo: (...args: any[]) => any;
    }
}

UniversalCamera.prototype.moveTo = (...args: any[]) => {
    console.log(args);
};
2 Likes

thanks for showing a working example, will mark your ans as solution.

so the key here is using named import such that the imported items are not under a TS namespace?

I will stick with my helper func though, keeping the namespaces make copying and pasting playground code frictionless :grin:

2 Likes

Hello,
This solution does not work for typescript within the PlayGround.

declare module "@babylonjs/core/Cameras/freeCamera" {
    interface FreeCamera {
        moveTo: (...args: any[]) => any;
    }
}

FreeCamera.prototype.moveTo = (...args: any[]) => {
    console.log(args);
};

if you cut&past this code into newly created playgroung typescript scene, the moveTo is not recognized/accepted at all by the compiler. So the augmentation is not take in account… i guess it’s because reaching the point the Babylon is not longer in TS but in JS… and the compiler is looking for the d.ts type definition, and the module name is wrong… i do not know how it’s configured behind the scene… any tricks ???

BTW replacing module with namespace is working…

declare namespace BABYLON {
    interface FreeCamera {
        moveTo: (...args: any[]) => any;
    }
}

BABYLON.FreeCamera.prototype.moveTo = (...args: any[]) => {
    console.log(args);
};

That’s because the playground is using the UMD packages. Thanks for posting the correct way of doing it in the playground :slight_smile:

1 Like