File: bestPractices.md

package info (click to toggle)
firefox 147.0-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 4,683,324 kB
  • sloc: cpp: 7,607,156; javascript: 6,532,492; ansic: 3,775,158; python: 1,415,368; xml: 634,556; asm: 438,949; java: 186,241; sh: 62,751; makefile: 18,079; objc: 13,092; perl: 12,808; yacc: 4,583; cs: 3,846; pascal: 3,448; lex: 1,720; ruby: 1,003; php: 436; lisp: 258; awk: 247; sql: 66; sed: 54; csh: 10; exp: 6
file content (164 lines) | stat: -rw-r--r-- 4,970 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# Best Practices

* [Enable JSDoc Linting](#enable-jsdoc-linting)
* [Documenting Types](#documenting-types)
  * [Avoid Missing Type Annotations](#avoid-missing-type-annotations)
  * [Variables](#variables)
  * [Avoid object types](#avoid-object-types)
  * [Import Types from Other Files](#import-types-from-other-files)
* [XPCOMUtils.declareLazy / defineLazy](#xpcomutilsdeclarelazy--definelazy)

## Enable JSDoc Linting
ESLint supports linting of JSDoc comments. Enabling the rules on your component
will help to ensure that you avoid missing or incorrect type definitions for function parameters.

You can check if your component is already covered by ensuring it is not in the
`rollout-valid-jsdoc` or `rollout-require-jsdoc` sections of the
[eslint-rollouts.config.mjs](https://searchfox.org/firefox-main/source/eslint-rollouts.config.mjs)
file in the top level of `firefox-main`.

## Documenting Types

### Avoid Missing Type Annotations
By default, if no type annotation is given and it cannot be inferred, then
TypeScript will assign that variable the `any` type. This is a special type that
skips type checking, and therefore may cause hidden failures.

For class members and functions this means adding type definitions for all the
parameters, e.g.

```js
class Foo {
  /**
   * Stores the search string. The type here could be omitted if the property is assigned in
   * the constructor of the class.
   *
   * @type {string}
   */
  #search;

  /**
   * Details about the function.
   *
   * @param {string} searchString
   *   Param documentation.
   * @param {object} previousResult
   *   Param documentation.
   * @param {nsIAutoCompleteObserver} listener
   *   Param documentation.
   */
  startSearch(searchString, previousResult, listener) {}
}
```

### Variables
Variable types will be inferred from the item that they are initially assigned
to. However, sometimes you may need to define the type appropriately, especially
for sets and maps.

```js
// This will be inferred as type string.
let foo = "bar";

// This needs the type defining.
/** @type {Map<string, number>} */
let baz = new Map();
baz.set("star", 1701);
```

### Avoid object types
`object` types are treated much the same as `any` - there is no type checking
performed on them.

Ideally all types should be defined. There are two ways to do this.

The first is within JSDoc comments:

```js
/**
 * @typedef {object} submissionMapEntry
 * @property {SearchEngine} engine
 *   The search engine.
 * @property {string} termsParameterName
 *   The search term parameter name.
 */

/**
 * Alternately for function parameters:
 *
 * @param {object} options
 * @param {boolean} options.option1
 *   A required boolean property within options.
 * @param {number} [options.option2]
 *   An optional number property within options.
 */
function myFunc(options) {}
```

This may then be used within the file.

The second way may be more appropriate if a type is used widely within a
component. You can create a `types/urlbarType.d.ts` file, reference it from the
`tsconfig.json` file and include a definition such as:

```js
/**
 * A structure that holds the details of commands for results.
 */
type UrlbarResultCommand = {
  /**
   * The name of the command. Must be specified unless `children` is present.
   * When a command is picked, its name will be passed as `details.selType` to
   * `onEngagement()`. The special name "separator" will create a menu separator.
   */
  name?: string;
  /**
   * An l10n object for the command's label. Must be specified unless `name`
   * is "separator".
   */
  l10n?: L10nIdArgs;
  /**
   * If specified, a submenu will be created with the given child commands.
   */
  children?: UrlbarResultCommand[];
};
```

:::{note}
If you are sharing object types outside of your component, prefer using a proper
class definition or other structure.
:::

### Import Types from Other Files

You may import types from other files to be able to reuse them.

```js
/**
 * @import { LangTags } from "./translations.d.ts"
 */

/**
 * @import { OpenedConnection } from "resource://gre/modules/Sqlite.sys.mjs"
 */
```

A `*.d.ts` file is a special type-definition file for TypeScript. These should
generally only be used for types that are internal to your component and are
not exposed outside.

## XPCOMUtils.declareLazy / defineLazy

These are newer functions that combine the existing
[XPCOMUtils](https://searchfox.org/firefox-main/source/js/xpconnect/loader/XPCOMUtils.sys.mjs)
functions into formats that are compatible with TypeScript.

Defining lazy objects with these functions provides the appropriate information
for TypeScript to be able to know the types on the lazy objects. Otherwise the
lazy objects will be defined as `any`.

:::{note}
It is suggested that these are not generally used until the functions have been
[moved to ChromeUtils](https://bugzilla.mozilla.org/show_bug.cgi?id=1992437),
to avoid needing to import `XPCOMUtils` everywhere.
:::