| name | typescript |
| description | Write TypeScript that satisfies this repo's lint and tsconfig rules, and convert an existing .js file to .ts without breaking its call sites. Use when adding or editing a .ts file, converting a .js file, adding the first .ts browser spec, or hitting a type-check or ESLint failure. |
TypeScript in this repo
New code is .ts, including tests, scripts, and Vite plugins. Do not add new
.js. testem.cjs is the only CommonJS file.
npm run type-check (tsc --noEmit) and npm run lint both gate PRs via the
Lint workflow. Run npm run type-check and npm run lint:fix after any edit
under sources/, scripts/, vite/, or tests/ — all four are in
tsconfig.json include. tsc is the typescript package, which this repo
aliases to @typescript/typescript6. @typescript/native is also in
devDependencies and is unused by workflows.
Erasable syntax only
Node runs first-party .ts by type-stripping it, with no tsx and no compile
step. erasableSyntaxOnly is on, so no enums, no namespaces, no parameter
properties. Anything that would need to emit runtime code is a type error.
Relative imports use the extension of the file on disk, so a .ts file
importing a still-.js module writes .js:
import { markNonExecutableLinesInLcov } from "./mark-non-executable-lines.js";
allowImportingTsExtensions is on, so importing ./foo.ts is correct once the
target is converted. Getting this backwards is the most common failure when
converting a file — every importer has to move with it.
tsconfig deliberately relaxes three strictest flags
tsconfig.json extends @tsconfig/strictest and then
turns off noUncheckedIndexedAccess, exactOptionalPropertyTypes, and
noPropertyAccessFromIndexSignature, each with a comment explaining why. Do not
re-enable them as a drive-by; pixel-index loops and metadata config bags depend
on them being off.
Lint rules that bite
- Unused bindings that must exist need a
_ prefix. argsIgnorePattern,
varsIgnorePattern, and caughtErrorsIgnorePattern are all ^_.
console.* is an error except console.error. Use console.error, or
debugLog / debugWarn / debugGroup / debugGroupEnd / debugTable from
sources/utils/debug.ts, which are gated
on localhost or ?debug=.
eslint.config.js applies
typescript-eslint's recommended preset to **/*.ts only, and layers
per-directory blocks for sources/, scripts/ + vite/, tests/,
tests/node/, and tests/visual/. A converted file moves from the .js
block to the .ts block and can pick up rules it was never subject to, so
lint the file after renaming, not before.
Converting a .js file
Four non-test .js files remain:
The rest are under tests/. Ordinary *_spec.js files are not named by
hardcoded path. These harness files are:
tests/tests.js — named in tests_run.html
tests/bdd-globals.js — the mocha-globals alias target in
vite.config.ts, in two HTML import maps
(scripts/zip/zip-export-profile-runner.html,
tests/fixtures/issue-382/issue382-golden-runner.html), and asserted by path
in tests/node/scripts/generateSources/vite_config_factory_and_resolve_spec.ts
tests/node/run-node-tests.js — named in
testem.cjs before_tests
tests/vitest-setup.js — imported by tests/tests.js
tests/testem-firefox-user.js — named in testem.cjs
Before renaming anything, grep for the filename. Breaking a harness path fails
a command rather than the type-check:
Checklist for a conversion:
git mv the file, then update every importer to the new extension.
- Update hardcoded paths in
package.json, testem.cjs, HTML files, and any
spec that asserts on the path.
npm run type-check — the file is newly checked, so expect real errors.
npm run lint:fix — it may have moved into a stricter ESLint block.
- Run whatever command names the file, not just the test suite. A broken
before_tests path or import map does not surface as a test failure.
- Check
codecov.yml ignore:. If the file is not
ignored, it now owes covered lines: coverage.
The first .ts browser spec
There is no precedent to copy yet: every browser spec imported from
tests/tests.js is still *_spec.js, and no
*_spec.ts exists outside tests/node/. Two things differ from the lines
around it:
-
Register it with its real extension, since relative imports use the
on-disk extension:
import "./components/MyComponent_spec.ts";
-
It is type-checked under @tsconfig/strictest as soon as it exists, which
existing .js specs are not (checkJs is off). Run npm run type-check;
expect to annotate DOM hosts and catalog handles, as in the example in
CONTRIBUTING.md.
Everything else about writing the spec is unchanged:
write-spec.