Skip to content

Commit c8a10b5

Browse files
committed
refactor(types): Add incremental js checking
1 parent 22be252 commit c8a10b5

File tree

9 files changed

+91
-0
lines changed

9 files changed

+91
-0
lines changed

.github/workflows/dev.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ jobs:
2424
run: npm run markdownlint
2525
- name: format code
2626
run: npm run prettier-check
27+
- name: tsc
28+
run: npm run tsc
2729
coverage:
2830
runs-on: ubuntu-latest
2931
env:

.github/workflows/prod.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ jobs:
2020
run: npm run markdownlint
2121
- name: format code
2222
run: npm run prettier-check
23+
- name: tsc
24+
run: npm run tsc
2325
deploy:
2426
runs-on: ubuntu-latest
2527
env:

Documentation/Contributors/CodingGuide/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ To some extent, this guide can be summarized as _make new code similar to existi
1919
- [Formatting](#formatting)
2020
- [Spelling](#spelling)
2121
- [Linting](#linting)
22+
- [Type Checking](#type-checking)
2223
- [Units](#units)
2324
- [Basic Code Construction](#basic-code-construction)
2425
- [Functions](#functions)
@@ -172,6 +173,34 @@ try {
172173
/*eslint-enable no-empty*/
173174
```
174175

176+
## Type Checking
177+
178+
We are incrementally adopting [type checking for JavaScript files](https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html). As of January 2026, type checks are enabled on a file-by-file basis, with `// @ts-check` annotations at the top of a file used to opt in. As adoption progresses, we expect this pattern will eventually be flipped to an opt-opt annotation instead. To see type system hints and errors in some editors or IDEs, you may need to configure or install TypeScript language server support. When in doubt, VSCode supports TypeScript language services [by default](https://code.visualstudio.com/docs/nodejs/working-with-javascript).
179+
180+
**References:**
181+
182+
For developers already familiar with TypeScript, writing JavaScript with JSDoc type annotations has some differences. For a summary of the annotation syntax, and supported features in JSDoc vs. TSDoc, see:
183+
184+
- [Type Checking JavaScript Files \| TypeScript](https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html)
185+
- [JSDoc Reference \| TypeScript](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html)
186+
- [JSDoc Cheatsheet and Type Safety Tricks](https://docs.joshuatz.com/cheatsheets/js/jsdoc/) by Joshua Tzucker
187+
188+
**Guidelines:**
189+
190+
1. **Incremental adoption with `@ts-check` annotations.** Enabling type checks for existing and new JavaScript files, using `// @ts-check` annotations, is encouraged but not required in the course of ongoing feature development and maintenance. If type-related changes are noisy enough to obscure the functional changes your PR, consider splitting the type fixes into a separate PR.
191+
2. **Common JavaScript/JSDoc obstacles.** JSDoc-based type checks have limited support for some coding patterns used in the CesiumJS codebase, such as classic prototype-based inheritance and `Object.defineProperties()`. In most cases these problems can be solved by refactoring with newer coding patterns, by hand or with tooling like [lebab](https://github.com/lebab/lebab). In other cases the solution may be less obvious, and a temporary skip-check annotation may be used to satisfy the type system. Example with lebab:
192+
```bash
193+
npx lebab packages/engine/Source/Core/Cartesian2.js \
194+
-o packages/engine/Source/Core/Cartesian2.js --transform class
195+
```
196+
3. **Prefer `@ts-expect-error` over `@ts-ignore`.** Sometimes a source file's types are _mostly_ consistent, but a few stubborn errors remain unsolved. These may be blocked by out-of-scope work, or may simply be unjustifiably difficult to solve in JSDoc-based types. Use `@ts-expect-error` annotations to relax the type system for specific lines. Unlike `@ts-ignore`, `@ts-expect-error` will raise errors when the line no longer contains an error, making it easier to clean up annotations later on.
197+
4. **Prefer `unknown` or `@ts-expect-error` over `any`.** Casting types to `any` forces them to be accepted everywhere, and effectively disables all type-checking dependent on that type. This tends to propagate throughout a type system, reducing the effectiveness of type checks, and should be avoided when possible. Prefer casting to `unknown`, or using a `@ts-expect-error` annotation. For example, when defining an object with string keys, with values whose types we don't care about, prefer `Record<string, unknown>` to `Record<string, any>`.
198+
5. **Type-only imports.** When JSDoc annotations depend on types not otherwise imported in a source file, it will be necessary to tell TypeScript where to find them. To avoid otherwise-unused imports, use type-only imports in separate one-line JSDoc comments:
199+
```javascript
200+
/** @import Cartesian3 from './Cartesian3.js'; */
201+
/** @import Cartesian4 from './Cartesian4.js'; */
202+
```
203+
175204
## Units
176205

177206
- Cesium uses SI units:

Tools/jsdoc/cesiumTags.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,13 @@ exports.defineTags = function (dictionary) {
5959
canHaveName: true,
6060
mustHaveValue: true,
6161
});
62+
63+
// Allow @import tags for type-only imports. Ignored by JSDoc, but resolved by tsc.
64+
// https://github.com/microsoft/TypeScript/issues/22160#issuecomment-2021459033
65+
// https://devblogs.microsoft.com/typescript/announcing-typescript-5-5-beta/#type-imports-in-jsdoc
66+
dictionary.defineTag("import", {
67+
canHaveType: true,
68+
canHaveName: true,
69+
mustHaveValue: true,
70+
});
6271
};

gulpfile.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,28 @@ export async function buildTs() {
252252
await createTypeScriptDefinitions();
253253
}
254254

255+
export async function tsc() {
256+
let workspaces;
257+
if (argv.workspace && !Array.isArray(argv.workspace)) {
258+
workspaces = [argv.workspace];
259+
} else if (argv.workspace) {
260+
workspaces = argv.workspace;
261+
} else {
262+
workspaces = getWorkspaces(true);
263+
}
264+
265+
for (const workspace of workspaces) {
266+
const directory = workspace
267+
.replace(`@${scope}/`, "")
268+
.replace(`packages/`, "");
269+
270+
const tsconfigPath = `packages/${directory}/tsconfig.json`;
271+
if (existsSync(tsconfigPath)) {
272+
execSync(`npx tsc --project ${tsconfigPath}`, { stdio: "inherit" });
273+
}
274+
}
275+
}
276+
255277
const filesToClean = [
256278
"Source/Cesium.js",
257279
"Source/Shaders/**/*.js",

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@
122122
"build-docs": "gulp buildDocs",
123123
"build-docs-watch": "gulp buildDocsWatch",
124124
"eslint": "eslint \"./**/*.*js\" \"./**/*.*ts*\" \"./**/*.html\" --cache --quiet",
125+
"tsc": "gulp tsc",
125126
"make-zip": "gulp -f gulpfile.makezip.js makeZip",
126127
"markdownlint": "markdownlint \"**/*.md\"",
127128
"release": "gulp release",
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default {
2+
// https://github.com/lint-staged/lint-staged#how-can-i-resolve-typescript-tsc-ignoring-tsconfigjson-when-lint-staged-runs-via-husky-hooks
3+
"*.{js,cjs,mjs,ts,cts,mts}": [() => "tsc"],
4+
};

packages/engine/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"build": "gulp build --workspace @cesium/engine",
6161
"build-ts": "gulp buildTs --workspace @cesium/engine",
6262
"coverage": "gulp coverage --workspace @cesium/engine",
63+
"tsc": "gulp tsc --workspace @cesium/engine",
6364
"test": "gulp test --workspace @cesium/engine",
6465
"postversion": "gulp postversion --workspace @cesium/engine"
6566
},

packages/engine/tsconfig.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"include": ["Source/**/*.js"],
3+
"compilerOptions": {
4+
// Module configuration.
5+
"moduleResolution": "bundler",
6+
"module": "ES2022",
7+
"target": "ES2022",
8+
9+
// I/O.
10+
"noEmit": true,
11+
"allowJs": true,
12+
13+
// Disabled by default. Individual JS files may opt-in to type checking
14+
// by including a `// @ts-check` comment at top of file.
15+
"checkJs": false,
16+
17+
// Checking declarations in dependencies is less important and less
18+
// actionable than checking source JS. Skip until checkJS is on, at least.
19+
"skipLibCheck": true
20+
}
21+
}

0 commit comments

Comments
 (0)