Instantiate threejs objects with typescript?

Hi guys,

I am trying to create a react component library that uses threejs, but when I am importing it into our consuming app, I get the following error:
image

This only happens when I am instantiating threejs specific objects (THREE.Vector3 in this case)

import { Vector3 } from "three";

import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
extend({ OrbitControls });

const Cube = () => {
  const { camera, gl, scene } = useThree();

  return (
    <>
      <orbitControls
        args={[camera, gl.domElement]}
        maxPolarAngle={Math.PI / 2.2}
        minPolarAngle={Math.PI / 20}
        maxDistance={8}
        minDistance={1}
        enablePan={false}
        target={new Vector3(0, 1.5, 0)} //this line breaks
      />
      <EnvironmentScene />

    </>
  );
};

export default Cube;

Once I comment out the target field, everything seems to work, so I guess I still miss some TS configuration?.

use

target={[0, 1.5, 0]}

Just a suggestion, use OrbitControls from @react-three/drei, then you don’t need to

extend({ OrbitControls });

E.g., Something minimal just to show you how you might use it

import { Canvas } from '@react-three/fiber'
import { OrbitControls } from '@react-three/drei'

export default function App() {
  return (
    <Canvas>
      <mesh>
        <boxGeometry />
        <meshBasicMaterial color={'lime'} wireframe />
      </mesh>
      <OrbitControls
        maxPolarAngle={Math.PI / 2.2}
        minPolarAngle={Math.PI / 20}
        maxDistance={8}
        minDistance={1}
        enablePan={false}
        target={[0, 1.5, 0]}
      />
    </Canvas>
  )
}

1 Like

Thank you so much!

I also have some custom geometries which extends BufferGeometry, do you have a suggestion there too how I could resolve the issue?

class CustomGeometry extends BufferGeometry{


...
    // build geometry
    this.setAttribute("position", new Float32BufferAttribute(verticesArray, 3));
    this.setAttribute("uv", new Float32BufferAttribute(uvArray, 2));
...
}

And how I try to use it:

const materialFront = new THREE.MeshPhysicalMaterial({
  color: new THREE.Color("red"),
  ...
});
const materialSide = new THREE.MeshPhysicalMaterial({
  color: new THREE.Color("blue"),
  ...
});


const CustomMesh = () => {
  const geom = useMemo(() => {
    const geom = new CustomGeometry();

   //some faces need different materials
    geom.groups.forEach((g, idx) => {
      if (idx === 1) g.materialIndex = 1;
      else g.materialIndex = 0;
    });

    return geom;
  }, []);

  return (
    <group>
      <mesh
        material={[materialFront, materialSide]} 
         geometry={geom}
      >
      </mesh>
    </group>
  );
};

This custom geometry retrieves error:

CustomGeometry.js:15 Uncaught TypeError: Class extends value undefined is not a constructor or null

And I think I will also have issues with material declaration like I did above (tried to avoid to create it as a component, because later on I want it to be reusable in order to save some memory.

it’s true that [x, y, z] is better, see React Three Fiber Documentation but your code is still valid. if you can’t execute new Vector3() then something must be wrong with your threejs install. maybe an ancient version that doesn’t agree with class semantics, or maybe a version mismatch where you end up with multiple three because somewhere there is a typo.

1 Like

I am using

    "@types/three": "^0.155.0",
    "three": "^0.155.0",

and when in VS I try to jump to the definition of Vector3, it jumps to @types/three/src/math/Vector3.d.ts

so I assume this part should be fine. The consuming app uses Typescript, so I thought maybe I missed to create a .d.ts and there declare the threejs module? I have really no experience in TS so I am not sure what I would need to do on my side.

looks good, but the error comes from javascript directly, doesn’t look like a typescript problem unless it messes for module resolution somehow.

i would isolate the issue, remove everything that has to do with react and just write new Vector3() then work from there. what are you using btw, vite?

I think certain tsconfig.json settings might cause your imports to work differently, do you mind sharing that? I might do something like this, if I’m publishing “modern” JavaScript, or have another tool handle transpiling.

...
"compilerOptions": {
    "esModuleInterop": true,
    "moduleResolution": "NodeNext",
    "module": "NodeNext",
    "target": "ESNext",
    ...
}

I removed everything, and the error is still the same what I did is:

  • npx create-react-app mylib

added babel + three in package.json, so it looks like:

{
  "name": "mylib",
  "version": "0.1.0",
  "private": true,
  "main": "./lib/index.js",
  "dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4",
    "@types/three": "^0.155.0",
    "three": "^0.155.0"
  },
  "devDependencies": {
    "@babel/cli": "^7.12.1",
    "@babel/core": "^7.21.0",
    "@babel/helper-builder-react-jsx": "^7.12.13",
    "@babel/plugin-proposal-class-properties": "^7.8.3",
    "@babel/plugin-transform-react-jsx": "^7.12.17",
    "@babel/plugin-transform-runtime": "^7.17.0",
    "@babel/plugin-transform-typescript": "^7.8.3",
    "@babel/preset-env": "^7.20.2",
    "@babel/preset-react": "^7.17.12",
    "@babel/preset-typescript": "^7.22.15",
    "@babel/runtime": "^7.8.4",
    "@testing-library/dom": "^9.3.1",
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "^14.0.0",
    "@testing-library/user-event": "^14.4.3",
    "babel-jest": "^29.4.3"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "npx babel ./node_modules/.bin/babel ./src -d ./lib --config-file ./babel_override.js --copy-files",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

I only have an index.js:

export { default as Cube } from "./Cube";

and the Cube.jsx:

import React from "react";
import * as THREE from "three";

const Cube = () => {
  const vec = new THREE.Vector3(1, 0, 1);  // this line causes the error

  return <div>Hello</div>;
};

export default Cube;

The babel_override:

module.exports = {
	overrides: [
		{
			presets: [
				[
					"@babel/preset-env",
					{ useBuiltIns: "usage", corejs: 3, targets: "defaults" },
				],
				["@babel/preset-react", { runtime: "automatic" }],
			],
			plugins: [
				"@babel/plugin-proposal-class-properties",
				"@babel/plugin-transform-react-jsx",
			],
		},
	],
};

and the .babelrc:

{
	"presets": [
		["@babel/preset-env", { "targets": { "node": "current" } }],
		["@babel/preset-react", { "runtime": "automatic" }],
		"@babel/preset-typescript"
	]
}

If I remove the Vector3 instantiation and rebuild the lib, then no issues and I see the element. We are using webpack

after I reproduced the issue with a minimal setup, only the consuming app has the tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2016", 
    "lib": ["dom", "dom.iterable", "esnext"], 
    "allowJs": true, 
    "skipLibCheck":true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true, 
    "strict": true,
    "forceConsistentCasingInFileNames": true, 
    "noFallthroughCasesInSwitch": true, 
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true, 
    "isolatedModules": true,
    "noEmit": true, 
    "jsx": "react" 
  },
  "include": ["src"],
  "exclude": [
    "node_modules",
    "output",
    "docker",
    "__mocks__",
    "src/api",
    "src/configs",
    "src/assets",
    "src/styles"
  ] 
}

There is a temporary solution:

copying three.module.js from node_modules/three/build into the source directory, and import Vector3 from there:

import {Vector3} from "../modules/three.module"

then after building with babel, the error has gone. If anyone could provide a nicer solution, do not hesitate please!

i would switch to vite. create-react-app is abandoned. you don’t have to do anything any longer, no more messing with typescript or babel, everything should just work. copying files into src seems like a hack to me.