Skip to content

TypeScript

You may want to use TypeScript when developing your React Three Fiber applications.

TypeScript is a tool that you can use when developing, to help you make sure all your code is type safe. I.e., if you need to pass an integer to a function, but you write it as a string instead, then the TypeScript compiler will notify you while developing.

Using TypeScript is optional, and many syntax issues can already be found if using ESLint as described at ESLint.

Using TypeScript in a React application requires some extra considerations since types are not automatically inferred when using hooks such as the useRef hook.

Visit the official Pmdrns.docs for in introduction of using TypeScript with React Three Fiber.

Continued below on this page is some extra information based on my personal experience.

So, starting from one of the courses React Three Fiber boilerplate branches, I will use the Leva branch.

git clone https://github.com/Sean-Bradley/React-Three-Fiber-Boilerplate.git
cd React-Three-Fiber-Boilerplate
git checkout leva
npm install
npm start

Rename extensions from jsx to tsx

Rename files with the jsx extension, to use the tsx extension.

App.jsx --> App.tsx
index.jsx --> index.tsx
Polyhedron.jsx --> Polyhedron.tsx

The files may now show some errors and warnings when viewing in the IDE.

Rename

Install TypeScript

Press CtrlC a few times to stop the development webserver and execute,

npm install typescript --save-dev

Note that when installing typeScript@5.x.x, react-scripts@5.0.1 will have a conflict.

We can fix it now by adding an overrides section to your package.json that matches the version of TypeScript installed. This fix is only required if using react-scripts@5.0.1.

//  ... existing package.json
  },
  "devDependencies": {
    "typescript": "^5.1.3"
  },
  "overrides": {
    "typescript": "^5.1.3"
  }
}

Add tsconfig.json

Add a tsconfig.json to your projects root folder. Add the contents below and save.

{
  "include": ["./src/**/*"],
  "compilerOptions": {
    "strict": true,
    "esModuleInterop": true,
    "lib": ["dom", "es2015"],
    "jsx": "react-jsx"
  }
}

tsconfig.json

Install Type Definitions

The IDE will read your code and try to match the types to it. You can tell it which definition files to use.

npm install @types/node --save-dev
npm install @types/react --save-dev
npm install @types/react-dom --save-dev
npm install @types/three --save-dev

After installing type definitions, it can take some time for the IDE to catch up. Try pressing F1, and select "Restart TS Server". This technique can also work for CodeSandbox projects.

Also note that you should try to match the versions of the types with the library installed.

E.g., If you are using Three@0.152.1 then install the closest @types/three version such as,

npm install @types/three@0.152.1 --save-dev

Note

Since the type definitions are developed separately from the main library, the latest version numbers won't always be in sync. Just try to match the closest number that you can, and you may not experience any issues unless you are using some part of the library where the method signature was recently changed.

Fix index.tsx

In index.tsx, the IDE is unsure what type is being returned from the document.getElementById method. We can cast the type as a HTMLDivElement.

Change the createRoot line from,

createRoot(document.getElementById('root')).render(

to

createRoot(document.getElementById('root') as HTMLDivElement).render(

When working with TypeScript, you often need to figure out the correct type of variable, or the value a method returns.

If you get stuck trying to figure out the most appropriate types, then you can cast as any.

createRoot(document.getElementById('root') as any).render(

Or, you can add the directive // @ts-ignore before the line containing the error.

// @ts-ignore
createRoot(document.getElementById('root')).render(

Fix Polyhedron.tsx

There are several problems in this file, lets look at Binding element 'polyhedron' implicitly has an 'any' type

The Polyhedron function is using destructuring assignment and a rest (...) operator.

Since when creating the Polyhedron, which is based on a THREE.Mesh, but with some extra custom attributes, I have chosen to create an interface named IPolyhedron which extends from the R3F MeshProps.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import { MeshProps } from '@react-three/fiber'
import { useRef, useState } from 'react'

interface IPolyhedron extends MeshProps {
  polyhedron: THREE.BufferGeometry[]
  color: THREE.Color | String
}

export default function Polyhedron({
  polyhedron,
  color,
  ...props
}: IPolyhedron) {
  const ref = useRef<THREE.Mesh>(null!)
  const [count, setCount] = useState(2)

  console.log(polyhedron[count].uuid)

  return (
    <mesh
      {...props}
      ref={ref}
      onPointerDown={() => {
        setCount((count + 1) % 3)
      }}
      geometry={polyhedron[count]}>
      <meshBasicMaterial color={color as THREE.Color} wireframe />
    </mesh>
  )
}

Also, since there is a useRef hook pointing to a mesh JSX element, I have declared it as,

const ref = useRef<THREE.Mesh>(null!)

Also, I needed to cast color as THREE.Color to avoid a problem in App.tsx where it passed the value as a string, and Leva could pass as a color, but the JSX in Polyhedron.tsx requires it to be a THREE.Color.

// line 20 App.tsx
color: {
  value: 'lime'
}
// line 6 Polyhedron.tsx optional THREE.Color or String
color: THREE.Color | String
// line 27 Polyhedron.tsx
<meshBasicMaterial color={color as THREE.Color} wireframe />

npm start

All problems should now be solved, we can run,

npm start
Leva (TypeScript version) CodeSandbox
Materials (TypeScript version) CodeSandbox
useGLTF (TypeScript version) CodeSandbox

Working Example

GitHub Branch

git clone https://github.com/Sean-Bradley/React-Three-Fiber-Boilerplate.git
cd React-Three-Fiber-Boilerplate
git checkout leva-typescript
npm install
npm start

Troubleshooting

'React' refers to a UMD global, but the current file is a module. Consider adding an import instead.

Ensure you have a tsconfig.json and that you have installed the type definitions for both React and React-dom.

npm install @types/react --save-dev
npm install @types/react-dom --save-dev

If using VSCode or CodeSandbox, then you can press F1 and select "Restart TS Server" to re-scan the type definitions.

TypeError: THREE.Vector3 is not a constructor

I have seen this happen when using react-scripts@5.0.1 with typescript@4.x.x and @react-three/drei.

Upgrade to TypeScript 5.

npm install typescript@latest

If still using react-scripts@5.0.1, then you will need to add an overrides section to your package.json that matches the version of TypeScript that you've installed.

//  ... existing package.json
    "typescript": "^5.1.3"
  },
  "overrides": {
    "typescript": "^5.1.3"
  }
}

npm ERR! While resolving: react-scripts@5.0.1

When executing npm install you get an error,

npm install
npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR!
npm ERR! While resolving: react-scripts@5.0.1
npm ERR! Found: typescript@5.x.x

See TypeError: THREE.Vector3 is not a constructor