Three.js with TypeScript: Setup and Common Gotchas

Three.js ships its own TypeScript type definitions built in (no separate @types/three package needed for current versions). Install three and start importing with full type support immediately. The main practical friction points are typing custom shader uniforms correctly and typing TSL node material properties, both of which have straightforward patterns once you've seen them once.

Last updated . Verified against three.js r181.

Read the FAQJump to code

Setup: it just works

Modern Three.js includes its own .d.ts type definitions in the package itself. npm install three and import * as THREE from 'three' gets full autocomplete and type checking with no separate types package. (Older project templates you might find online still reference @types/three as a separate install; that's no longer necessary for current Three.js versions and can actually cause version-mismatch type errors if installed alongside the built-in types.)

Typing custom shader uniforms

ShaderMaterial's uniforms object is typed as a fairly loose { [uniform: string]: IUniform } by default, which means TypeScript won't catch a typo'd uniform name or a wrong value type at the uniforms object itself. The common pattern for stricter typing is defining your own uniforms interface and casting, or, often simpler in practice, switching custom shaders to TSL node materials, where uniform values are typed values from uniform() calls rather than untyped object properties, giving real type safety without extra interface boilerplate.

Common type errors and what they mean

  • "Property 'X' does not exist on type 'Object3D'": usually means you're accessing a property specific to a subclass (like Mesh.material or Light.intensity) on a variable typed as the more general Object3D. Fix by typing the variable as the specific subclass, or narrowing with an instanceof check (e.g. if (child instanceof THREE.Mesh)) when traversing a loaded scene graph of mixed object types.
  • Errors after a Three.js version upgrade: Three.js's public API occasionally changes between releases, and type definitions change with it; check the release's migration notes rather than assuming your code has a bug.
  • Type errors from a stale @types/three install: if you have the separate @types/three package still installed alongside a modern Three.js version, remove it. The version mismatch between the external types package and the library's own built-in types is a common source of confusing errors.

Code

Typed scene-graph traversal (r181)
1import * as THREE from 'three'
2
3function setShadowsRecursive(object: THREE.Object3D) {
4 object.traverse((child) => {
5 if (child instanceof THREE.Mesh) {
6 // TypeScript now knows child has .castShadow, .material, etc.
7 child.castShadow = true
8 child.receiveShadow = true
9 }
10 })
11}
Typed TSL uniforms (r181)
1import { uniform, color } from 'three/tsl'
2
3// uniform() infers its type from the initial value; no separate interface needed.
4const pulseSpeed = uniform(2.0) // typed as a float uniform
5const baseColor = uniform(color(0x6366f1)) // typed as a color uniform
6
7const material = new THREE.MeshStandardNodeMaterial()
8material.emissiveNode = baseColor.mul(pulseSpeed) // type-checked at compile time

Learn this properly

Learn Practical TSL

Your First Node Material

Every lesson in the course uses TSL's naturally type-safe node API.

Start the lesson (6 minutes)

Frequently asked questions

Do I need @types/three for TypeScript with Three.js?

No, not for current Three.js versions. The library ships its own built-in type definitions. Installing the separate @types/three package alongside it can actually cause version-mismatch type errors.

How do I type a custom Three.js ShaderMaterial's uniforms?

ShaderMaterial's uniforms object is loosely typed by default, so typos or wrong value types aren't caught automatically. Define your own uniforms interface and cast, or switch to TSL node materials, where uniform() calls are typed values with real type safety built in.

Why does TypeScript say a property doesn't exist on Object3D?

You're likely accessing a subclass-specific property (like .material on Mesh) on a variable typed as the more general Object3D. Type it as the specific subclass, or narrow with an instanceof check when traversing a mixed scene graph.

Keep reading