Request for Comment (RFC) and a proposal for adopting JSDoc within the three.js project. The proposal intends to mitigate or solve two longstanding problems:
- Unofficial type definitions: Increasingly, many web projects and IDEs rely on type definitions in libraries, which three.js does not natively provide. The community maintains type definitions in a separate repository, with some overhead in cost and potential for mistakes or delays.
- Cost of maintaining localized documentation: API documentation for each class is maintained in hand-written HTML files, one file for each {class, language} pair. Maintainers contribute to the English-language documentation, while volunteers are largely responsible for identifying where English translations have changed and updating the localized versions.
Required if RFC is accepted.
JavaScript source files will be annotated with comments in JSDoc format, describing only those type definitions required for public-facing APIs. Human-readable API descriptions are not present at this milestone. From the JSDoc comments, release builds will generate TypeScript type definitions published to npm within the library.
Example: `src/core/Object.js`
/**
* @property {true} isObject3D
* @property {string} uuid
* @property {number} id
* @property {string} name
* @property {string} type
* @property {Object3D|null} parent
* @property {Object3D[]} children
* @property {Vector3} up
* @property {Vector3} position
* @property {Euler} rotation
* @property {Vector3} scale
* ...
*/
class Object3D extends EventDispatcher {
constructor() { ... }
/**
* @param {WebGLRenderer} renderer
* @param {Scene} scene
* @param {Camera} camera
* @param {BufferGeometry} geometry
* @param {Material} material
* @param {any} group
*/
onBeforeRender() { ... }
/**
* @param {WebGLRenderer} renderer
* @param {Scene} scene
* @param {Camera} camera
* @param {BufferGeometry} geometry
* @param {Material} material
* @param {any} group
*/
onAfterRender() { ... }
/**
* @param {Matrix4} matrix
*/
applyMatrix4(matrix) { ... }
...
}
We'll add typescript
to package.json#devDependencies
, and create a tsconfig.json
file with checkJS
and allowJS
options enabled. Editors like VSCode should then automatically provide hints when types are invalid. The code will run as expected in a browser, regardless of whether types are updated or compiled. At build time, TypeScript compiles the JSDoc comments to build/three.d.ts
, including API definitions for the entire library — including three
and three/addons/*
. See dts-buddy
for details on how this should work.
Optional, and depends on Milestone 1.
In addition to type annotations, JSDoc comments will now include human-readable descriptions, in English, for each public-facing API item. In a build step, tooling will process the JSDoc comments or the d.ts
files, and generate corresponding HTML pages identical to our current pages.
What about inline examples?
If desired, examples may be specified in one of two ways:
/**
* Description of Mesh class here.
*
* @example link_to_example.html
*
* @example
* Inline example below:
* ```javascript
* import { Mesh } from 'three';
* const mesh = new Mesh( geometry, material );
* ```
*/
class Mesh {
}
The former requires external files, and the latter is more verbose. Either will require some work on the HTML generator, which I believe is manageable. For simplicity, it may be preferable to leave these examples out of the API documentation and link to other examples or the manual instead.
Optional, and depends on Milestones 2–3.
JSDoc or type definitions are processed in a build step, assigning a unique key to each English-language comment (e.g. Mesh#geometry
). For each target output language, a human readable file (likely .csv or .yaml) is generated with content equivalent to:
Example: lang_it.csv
key | enUpdated | itUpdated | en | it |
---|---|---|---|---|
Mesh#geometry | 2021-05-04 | 2021-10-15 | An instance of BufferGeometry... | Un'istanza di BufferGeometry... |
Mesh#material | 2021-05-04 | 2021-10-15 | An instance of Material... | Un'istanza di Material... |
The lastUpdated
column will be maintained by the generator, allowing translators to more easily find sections where the English comments have changed since the last translation. Optionally, translations may be modified in external software like Microsoft Excel or Google Sheets.
In a final build step, these translation files are processed, and translated HTML pages for API documentation are generated alongside the English-language versions.
- By generating API documentation automatically, the process for maintaining API docs diverges from the process for maintaining the manual and other written resources on the website.
- JSDoc type annotations are admittedly verbose, and less ergonomic to write than TypeScript annotations. This problem could be mitigated if proposals for JavaScript Type Annotations are approved and implemented.
-
Rewrite three.js in TypeScript. None of the primary maintainers of three.js are currently interested in a change toward writing and maintaining TypeScript code.
-
Maintain
d.ts
type definitions manually. Manually updatingd.ts
files was attempted as a trial for several three.js releases. Cost of maintenance was prohibitive, and inability to validate hand-writtend.ts
files against source files created unacceptable problems.
- TypeScript cannot parse
@property
or@member
directives in JSDoc, breaking use ofObject.defineProperty
. See list of supported tags.- microsoft/TypeScript#7237
- microsoft/TypeScript#28730
- microsoft/TypeScript#15715
- dsherret/ts-morph#1427 (possible workaround without the TypeScript compiler)
It may be worth pointing out what the same maintainers feel when types randomly get removed in a patch. However it has been clarified that three.js is exempt from this due to the personal relationship between the involved parties.
I'm one of these users that are affected by these types of changes. I volunteered my time to improve some types. One blocker seems to have been that for some reason, at the time, it had to be reviewed by you @donmccurdy when you yourself said that you didn't know TS as well at the time and were unfamiliar with some advanced usage (eg
infer
, i also never found an application for this).But others have volunteered to fill that void and bring TS experience to the table.
![image](https://user-images.githubusercontent.com/4681282/267157576-642bc4b4-9059-4247-aec5-75333d2b3e37.png)
The very next comment is this one way announcement that all types have been removed from three.js
If you can some how guarantee that it wont come to this:
![image](https://user-images.githubusercontent.com/4681282/267157893-3d4acc63-142c-4279-b44a-e961df18baf1.png)
this proposal would make sense. Otherwise, i have to agree with the Svelte maintainers, this whole thing is what they say it is - user hostile.
You will hurt users in the long run, DT works, it suffers from the same issues as every other package depending on three.js - the library breaks everyones project once per month.
You are solving for an issue that does not exist.
Consider proposing semver, and a stable three.js version. That would give a fighting chance to people to actually make some sane stable types. This is hard to do in general, let alone on a moving target.Svelte maintainers have released Svelte 1,2,3 and 4. This type of contract would make it orders of magnitude easier to write powerful types. This is next to impossible to do if these contracts change once per month. Listen to svelte maintainers, don't be user hostile, feed the poor.