TypeScript + Three.js bez kroku budowania — Import Maps, @types/three i typowe pułapki

Narzędzia do budowania są przydatne, ale nie są wymagane. Dzięki nowoczesnym import maps i modułom ES możesz uzyskać pełne bezpieczeństwo typów Three.js — autouzupełnianie, błędy typów, przechodzenie do definicji — bezpośrednio w VS Code bez uruchamiania choćby jednej komendy npm run build.

1. Import Maps dla modułów ES bez budowania

Import map to blok <script type="importmap"> w twoim HTML, który mapuje gołe specyfikatory modułów (jak 'three') na rzeczywiste adresy URL. Wszystkie nowoczesne przeglądarki obsługują to natywnie:

<script type="importmap">
{
  "imports": {
    "three":          "https://cdn.jsdelivr.net/npm/three@0.160/build/three.module.js",
    "three/addons/":  "https://cdn.jsdelivr.net/npm/three@0.160/examples/jsm/"
  }
}
</script>

<script type="module">
// ✅ Działa bezpośrednio w przeglądarce — bundler niepotrzebny
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const renderer = new THREE.WebGLRenderer({ antialias: true });
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 1000);
// ...
</script>

2. Typy TypeScript bez tsc

Serwer językowy TypeScript w VS Code (Pylance / tsserver) czyta definicje typów nawet dla zwykłych plików .js, jeśli dodasz jsconfig.json lub komentarz // @ts-check. Zainstaluj pakiet typów lokalnie (tylko dla edytora — nie jest dostarczany użytkownikom) i wskaż na niego w swojej konfiguracji:

# W katalogu twojego projektu:
npm install --save-dev three @types/three

# To instaluje tylko do node_modules — nic nie jest pakowane ani wysyłane
// jsconfig.json — włącza typy w VS Code dla plików .js
{
  "compilerOptions": {
    "checkJs": true,
    "strict": false,
    "moduleResolution": "bundler",
    "types": ["three"]
  },
  "include": ["**/*.js"],
  "exclude": ["node_modules"]
}

Teraz VS Code zna całe drzewo typów Three.js. Otrzymujesz autouzupełnianie, dokumentację po najechaniu kursorem oraz błędy typów w swoich plikach JavaScript — bez tsconfig.json i bez kroku kompilacji.

3. JSDoc jako alternatywa dla typowanego JavaScriptu

Jeśli chcesz zerowej zależności od Node.js (żadnego package.json), możesz dodać typy poprzez adnotacje JSDoc i odwołać się do definicji typów z CDN:

// @ts-check
/// <reference types="https://cdn.jsdelivr.net/npm/@types/three/index.d.ts" />

/** @type {import('three').PerspectiveCamera} */
const camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 1000);

/**
 * @param {import('three').Mesh} mesh
 * @param {import('three').Vector3} direction
 * @returns {void}
 */
function moveMesh(mesh, direction) {
  mesh.position.add(direction);
}

Odwołania do typów przez CDN działają w VS Code 1.88+ z silnikiem "typescript.preferences.useAliasesForRenames". VS Code pobiera plik .d.ts raz i go buforuje. To podejście stosowane jest we wszystkich symulacjach na tej stronie — bez narzędzi do budowania, pełny IntelliSense.

4. Typowe pułapki typów w projektach Three.js

Puste odniesienie do canvas po DOMContentLoaded
document.getElementById('canvas') zwraca HTMLElement | null. Konstruktor WebGLRenderer w Three.js oczekuje HTMLCanvasElement, a nie HTMLElement | null. Rzutuj jawnie: canvas as HTMLCanvasElement lub użyj asercji null wewnątrz strażnika: if (!(el instanceof HTMLCanvasElement)) return;
Object3D.getObjectByName zwraca nieznany typ
scene.getObjectByName('mesh') zwraca Object3D | undefined. Jeśli wiesz, że to Mesh, użyj asercji typu: scene.getObjectByName('mesh') as THREE.Mesh | undefined i obsłuż przypadek undefined przed dostępem do geometrii lub materiału.
Właściwości materiału nie istnieją w typie bazowym
mesh.material.color nie kompiluje się, ponieważ bazowy typ Material nie ma właściwości color. Musisz zawęzić do konkretnego typu: (mesh.material as THREE.MeshStandardMaterial).color.set('#ff0000') lub od razu zadeklarować mesh jako THREE.Mesh<THREE.BufferGeometry, THREE.MeshStandardMaterial>.
event.target w handlerze zdarzenia to nie obiekt Three.js
Przy korzystaniu z Raycaster właściwość object przecięcia jest typowana jako Object3D, a nie konkretny podtyp mesha. Użyj intersection.object instanceof THREE.Mesh jako strażnika typu przed dostępem do właściwości specyficznych dla mesha.
Uniformy w ShaderMaterial typowane zbyt luźno
ShaderMaterial.uniforms jest typowane jako { [uniform: string]: IUniform } — bardzo szeroko. Zdefiniuj typowaną stałą i rozszerz ją w materiale, by uzyskać autouzupełnianie dla swoich konkretnych nazw uniformów: const uniforms = { uTime: { value: 0 } } as const.

5. Zalecenia dotyczące trybu ścisłego

Nawet w konfiguracji opartej wyłącznie na JSDoc włączenie "strict": true w jsconfig.json wychwytuje powyższe pułapki null/undefined już na etapie edycji. Dla projektów Three.js najbardziej przydatne są następujące podopcje trybu ścisłego: