Enabling TypeScript for a Component
TypeScript checks front-end code as JavaScript. Writing code as TypeScript
(.ts) in firefox-main is not supported, and not planned to be supported.
You can enable it for a directory by adding a tsconfig.json file and adding
the directory to the TypeScript linter.
Steps
Add a
tsconfig.jsonto the directory (see The tsconfig.json File).Check the directory, and exclude the modules that fail (see Checking the Project).
Register the directory with the linter and with CI (see Registering with the Linter).
The tsconfig.json File
Start from this file, and adjust the ../../../ prefixes to the depth of your
directory:
{
"include": ["*.sys.mjs", "types/*.ts"],
"exclude": ["SomeModule.sys.mjs"],
"extends": "../../../tools/@types/tsconfig.json",
"compilerOptions": {
"checkJs": true,
"strict": true,
"noImplicitAny": false,
"strictNullChecks": false,
"plugins": [
{
"transform": "../../../tools/ts/plugins/checkRootOnly.js",
"transformProgram": true
}
]
}
}
extends points to tools/@types/tsconfig.json. The file provides
various set-up functionality to map module URLs to their sources and to provide
the Gecko DOM and XPCOM types.
It is suggested to keep the four compiler options as they are. Most projects
currently in the tree set them. Turning noImplicitAny or strictNullChecks on
adds a lot of errors and so may require their own work. Additionally they may be
easier to add in future, once there is more coverage and definition of types
across the tree.
The checkRootOnly plugin checks only the files that include matches and
exclude doesn’t remove. TypeScript still reads the modules those files import,
for their types, but doesn’t report errors in them. A file that starts with
// @ts-nocheck stays unchecked even when it is included.
Include Patterns
include patterns accept *, ? and **, but not braces, so content/*.js
and content/*.mjs are two possible entries. A pattern that matches nothing is
fine, as long as the project as a whole matches at least one file.
A non-module script, such as one that a page loads with a plain <script> tag,
can join the project through another include entry. Its top-level names don’t
collide with names in the modules, because each module has its own scope.
Browser Window Code
If modules in the directory run in the browser window, add
../../../tools/@types/browser-window.d.ts to include. The file declares the
globals that the scripts of browser.xhtml define.
Code that runs elsewhere but receives a browser window can annotate it as
BrowserWindow, which tools/@types/browser-window-globals.d.ts
declares.
Checking the Project
Check the directory, and add each module that fails to exclude:
./mach ts check path/to/dir
Landing the Project
The project should only be landed when the linter passes. It is acceptable to only have one or two modules enabled to begin with, and for follow-up bugs/patches to be used for enabling TypeScript on more modules.
Registering with the Linter
Add the directory to tools/lint/typescript.yml and to the
files-changed list of the typescript task in
taskcluster/kinds/source-test/mozlint.yml. Keep the two lists in
sync, as the comments in both files ask, and in alphabetical order. Each
directory takes two entries in files-changed:
- 'path/to/dir/**/*.*js'
- 'path/to/dir/**/*.ts'
typescript.yml lists only directories, so add the directory in the same patch
that adds its tsconfig.json.
The linter runs a project whenever a changed file is inside its directory, and
then checks the whole project. TypeScript still reports errors only in the files
that include lists. To run the check as CI does, but scoped to your project:
./mach lint -l typescript path/to/dir
What Might a Low Error Count Hide?
Even with an empty exclude list, TypeScript treats some values as any, and
it reports no errors for code that uses an any value. Three things cause this:
With
noImplicitAnyoff, a parameter without a JSDoc type is silentlyany. Documenting a module raises its error count, because each written type gives TypeScript something to check.ChromeUtils.defineESModuleGetters(lazy, …)makes every property oflazyany.XPCOMUtils.declareLazygives each one the type of its module (see Best Practices).The generated
Windowtype lets any property name through, so every property read off aWindowisany. PreferBrowserWindowfor a browser window.
Getting Help
If you get stuck, ask in the Lint & Formatting channel (#lint:mozilla.org) on Matrix.