|
| 1 | +# Enforces naming conventions for everything across a codebase (`naming-convention`) |
| 2 | + |
| 3 | +Enforcing naming conventions helps keep the codebase consistent, and reduces overhead when thinking about how to name a variable. |
| 4 | +Additionally, a well designed style guide can help communicate intent, such as by enforcing all private properties begin with an `_`, and all global-level constants are written in `UPPER_CASE`. |
| 5 | + |
| 6 | +There are many different rules that have existed over time, but they have had the problem of not having enough granularity, meaning it was hard to have a well defined style guide, and most of the time you needed 3 or more rules at once to enforce different conventions, hoping they didn't conflict. |
| 7 | + |
| 8 | +## Rule Details |
| 9 | + |
| 10 | +This rule allows you to enforce conventions for any identifier, using granular selectors to create a fine-grained style guide. |
| 11 | + |
| 12 | +### Note - this rule only needs type information in specific cases, detailed below |
| 13 | + |
| 14 | +## Options |
| 15 | + |
| 16 | +This rule accepts an array of objects, with each object describing a different naming convention. |
| 17 | +Each property will be described in detail below. Also see the examples section below for illustrated examples. |
| 18 | + |
| 19 | +```ts |
| 20 | +type Options = { |
| 21 | + // format options |
| 22 | + format: ( |
| 23 | + | 'camelCase' |
| 24 | + | 'strictCamelCase' |
| 25 | + | 'PascalCase' |
| 26 | + | 'StrictPascalCase' |
| 27 | + | 'snake_case' |
| 28 | + | 'UPPER_CASE' |
| 29 | + )[]; |
| 30 | + custom?: { |
| 31 | + regex: string; |
| 32 | + match: boolean; |
| 33 | + }; |
| 34 | + leadingUnderscore?: 'forbid' | 'allow' | 'require'; |
| 35 | + trailingUnderscore?: 'forbid' | 'allow' | 'require'; |
| 36 | + prefix?: string[]; |
| 37 | + suffix?: string[]; |
| 38 | + |
| 39 | + // selector options |
| 40 | + selector: Selector; |
| 41 | + filter?: string; |
| 42 | + // the allowed values for these are dependent on the selector - see below |
| 43 | + modifiers?: Modifiers<Selector>[]; |
| 44 | + types?: Types<Selector>[]; |
| 45 | +}[]; |
| 46 | + |
| 47 | +// the default config essentially does the same thing as ESLint's camelcase rule |
| 48 | +const defaultOptions: Options = [ |
| 49 | + { |
| 50 | + selector: 'default', |
| 51 | + format: ['camelCase'], |
| 52 | + leadingUnderscore: 'allow', |
| 53 | + trailingUnderscore: 'allow', |
| 54 | + }, |
| 55 | + |
| 56 | + { |
| 57 | + selector: 'variable', |
| 58 | + format: ['camelCase', 'UPPER_CASE'], |
| 59 | + leadingUnderscore: 'allow', |
| 60 | + trailingUnderscore: 'allow', |
| 61 | + }, |
| 62 | + |
| 63 | + { |
| 64 | + selector: 'typeLike', |
| 65 | + format: ['PascalCase'], |
| 66 | + }, |
| 67 | +]; |
| 68 | +``` |
| 69 | + |
| 70 | +### Format Options |
| 71 | + |
| 72 | +Every single selector can have the same set of format options. |
| 73 | +When the format of an identifier is checked, it is checked in the following order: |
| 74 | + |
| 75 | +1. validate leading underscore |
| 76 | +1. validate trailing underscore |
| 77 | +1. validate prefix |
| 78 | +1. validate suffix |
| 79 | +1. validate custom |
| 80 | +1. validate format |
| 81 | + |
| 82 | +At each step, if the identifier matches the option, the matching part will be removed. |
| 83 | +For example, if you provide the following formatting option: `{ leadingUnderscore: 'allow', prefix: ['I'], format: ['StrictPascalCase'] }`, for the identifier `_IMyInterface`, then the following checks will occur: |
| 84 | + |
| 85 | +1. `name = _IMyInterface` |
| 86 | +1. validate leading underscore - pass |
| 87 | + - Trim leading underscore - `name = IMyInterface` |
| 88 | +1. validate trailing underscore - no check |
| 89 | +1. validate prefix - pass |
| 90 | + - Trim prefix - `name = MyInterface` |
| 91 | +1. validate suffix - no check |
| 92 | +1. validate format - pass |
| 93 | + |
| 94 | +One final note is that if the name were to become empty via this trimming process, it is considered to match all `format`s. An example of where this might be useful is for generic type parameters, where you want all names to be prefixed with `T`, but also want to allow for the single character `T` name. |
| 95 | + |
| 96 | +#### `format` |
| 97 | + |
| 98 | +The `format` option defines the allowed formats for the identifier. This option accepts an array of the following values, and the identifier can match any of them: |
| 99 | + |
| 100 | +- `camelCase` - standard camelCase format - no underscores are allowed between characters, and consecutive capitals are allowed (i.e. both `myID` and `myId` are valid). |
| 101 | +- `strictCamelCase` - same as `camelCase`, but consecutive capitals are not allowed (i.e. `myId` is valid, but `myID` is not). |
| 102 | +- `PascalCase` - same as `camelCase`, except the first character must be upper-case. |
| 103 | +- `StrictPascalCase` - same as `strictCamelCase`, except the first character must be upper-case. |
| 104 | +- `snake_case` - standard snake_case format - all characters must be lower-case, and underscores are allowed. |
| 105 | +- `UPPER_CASE` - same as `snake_case`, except all characters must be upper-case. |
| 106 | + |
| 107 | +### `custom` |
| 108 | + |
| 109 | +The `custom` option defines a custom regex that the identifier must (or must not) match. This option allows you to have a bit more finer-grained control over identifiers, letting you ban (or force) certain patterns and substrings. |
| 110 | +Accepts an object with the following properties: |
| 111 | + |
| 112 | +- `regex` - accepts a regular expression (anything accepted into `new RegExp(filter)`). |
| 113 | +- `match` - true if the identifier _must_ match the `regex`, false if the identifier _must not_ match the `regex`. |
| 114 | + |
| 115 | +#### `leadingUnderscore` / `trailingUnderscore` |
| 116 | + |
| 117 | +The `leadingUnderscore` / `trailingUnderscore` options control whether leading/trailing underscores are considered valid. Accepts one of the following values: |
| 118 | + |
| 119 | +- `forbid` - a leading/trailing underscore is not allowed at all. |
| 120 | +- `allow` - existence of a leading/trailing underscore is not explicitly enforced. |
| 121 | +- `require` - a leading/trailing underscore must be included. |
| 122 | + |
| 123 | +#### `prefix` / `suffix` |
| 124 | + |
| 125 | +The `prefix` / `suffix` options control which prefix/suffix strings must exist for the identifier. Accepts an array of strings. |
| 126 | + |
| 127 | +If these are provided, the identifier must start with one of the provided values. For example, if you provide `{ prefix: ['IFace', 'Class', 'Type'] }`, then the following names are valid: `IFaceFoo`, `ClassBar`, `TypeBaz`, but the name `Bang` is not valid, as it contains none of the prefixes. |
| 128 | + |
| 129 | +### Selector Options |
| 130 | + |
| 131 | +- `selector` (see "Allowed Selectors, Modifiers and Types" below). |
| 132 | +- `filter` accepts a regular expression (anything accepted into `new RegExp(filter)`). It allows you to limit the scope of this configuration to names that match this regex. |
| 133 | +- `modifiers` allows you to specify which modifiers to granularly apply to, such as the accessibility (`private`/`public`/`protected`), or if the thing is `static`, etc. |
| 134 | + - The name must match _all_ of the modifiers. |
| 135 | + - For example, if you provide `{ modifiers: ['private', 'static', 'readonly'] }`, then it will only match something that is `private static readonly`, and something that is just `private` will not match. |
| 136 | +- `types` allows you to specify which types to match. This option supports simple, primitive types only (`boolean`, `string`, `number`, `array`, `function`). |
| 137 | + - The name must match _one_ of the types. |
| 138 | + - **_NOTE - Using this option will require that you lint with type information._** |
| 139 | + - For example, this lets you do things like enforce that `boolean` variables are prefixed with a verb. |
| 140 | + - `boolean` matches any type assignable to `boolean | null | undefined` |
| 141 | + - `string` matches any type assignable to `string | null | undefined` |
| 142 | + - `number` matches any type assignable to `number | null | undefined` |
| 143 | + - `array` matches any type assignable to `Array<unknown> | null | undefined` |
| 144 | + - `function` matches any type assignable to `Function | null | undefined` |
| 145 | + |
| 146 | +The ordering of selectors does not matter. The implementation will automatically sort the selectors to ensure they match from most-specific to least specific. It will keep checking selectors in that order until it finds one that matches the name. |
| 147 | + |
| 148 | +For example, if you provide the following config: |
| 149 | + |
| 150 | +```ts |
| 151 | +[ |
| 152 | + /* 1 */ { selector: 'default', format: ['camelCase'] }, |
| 153 | + /* 2 */ { selector: 'variable', format: ['snake_case'] }, |
| 154 | + /* 3 */ { selector: 'variable', type: ['boolean'], format: ['UPPER_CASE'] }, |
| 155 | + /* 4 */ { selector: 'variableLike', format: ['PascalCase'] }, |
| 156 | +]; |
| 157 | +``` |
| 158 | + |
| 159 | +Then for the code `const x = 1`, the rule will validate the selectors in the following order: `3`, `2`, `4`, `1`. |
| 160 | + |
| 161 | +#### Allowed Selectors, Modifiers and Types |
| 162 | + |
| 163 | +There are two types of selectors, individual selectors, and grouped selectors. |
| 164 | + |
| 165 | +##### Individual Selectors |
| 166 | + |
| 167 | +Individual Selectors match specific, well-defined sets. There is no overlap between each of the individual selectors. |
| 168 | + |
| 169 | +- `variable` - matches any `var` / `let` / `const` variable name. |
| 170 | + - Allowed `modifiers`: none. |
| 171 | + - Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. |
| 172 | +- `function` - matches any named function declaration or named function expression. |
| 173 | + - Allowed `modifiers`: none. |
| 174 | + - Allowed `types`: none. |
| 175 | +- `parameter` - matches any function parameter. Does not match parameter properties. |
| 176 | + - Allowed `modifiers`: none. |
| 177 | + - Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. |
| 178 | +- `property` - matches any object, class, or object type property. Does not match properties that have direct function expression or arrow function expression values. |
| 179 | + - Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`. |
| 180 | + - Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. |
| 181 | +- `parameterProperty` - matches any parameter property. |
| 182 | + - Allowed `modifiers`: `private`, `protected`, `public`, `readonly`. |
| 183 | + - Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. |
| 184 | +- `method` - matches any object, class, or object type method. Also matches properties that have direct function expression or arrow function expression values. Does not match accessors. |
| 185 | + - Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`. |
| 186 | + - Allowed `types`: none. |
| 187 | +- `accessor` - matches any accessor. |
| 188 | + - Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`. |
| 189 | + - Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. |
| 190 | +- `enumMember` - matches any enum member. |
| 191 | + - Allowed `modifiers`: none. |
| 192 | + - Allowed `types`: none. |
| 193 | +- `class` - matches any class declaration. |
| 194 | + - Allowed `modifiers`: `abstract`. |
| 195 | + - Allowed `types`: none. |
| 196 | +- `interface` - matches any interface declaration. |
| 197 | + - Allowed `modifiers`: none. |
| 198 | + - Allowed `types`: none. |
| 199 | +- `typeAlias` - matches any type alias declaration. |
| 200 | + - Allowed `modifiers`: none. |
| 201 | + - Allowed `types`: none. |
| 202 | +- `enum` - matches any enum declaration. |
| 203 | + - Allowed `modifiers`: none. |
| 204 | + - Allowed `types`: none. |
| 205 | +- `typeParameter` - matches any generic type parameter declaration. |
| 206 | + - Allowed `modifiers`: none. |
| 207 | + - Allowed `types`: none. |
| 208 | + |
| 209 | +##### Group Selectors |
| 210 | + |
| 211 | +Group Selectors are provided for convenience, and essentially bundle up sets of individual selectors. |
| 212 | + |
| 213 | +- `default` - matches everything. |
| 214 | + - Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`. |
| 215 | + - Allowed `types`: none. |
| 216 | +- `variableLike` - matches the same as `variable`, `function` and `parameter`. |
| 217 | + - Allowed `modifiers`: none. |
| 218 | + - Allowed `types`: none. |
| 219 | +- `memberLike` - matches the same as `property`, `parameterProperty`, `method`, `accessor`, `enumMember`. |
| 220 | + - Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`. |
| 221 | + - Allowed `types`: none. |
| 222 | +- `typeLike` - matches the same as `class`, `interface`, `typeAlias`, `enum`, `typeParameter`. |
| 223 | + - Allowed `modifiers`: `abstract`. |
| 224 | + - Allowed `types`: none. |
| 225 | + |
| 226 | +## Examples |
| 227 | + |
| 228 | +### Enforce that all variables, functions and properties follow are camelCase |
| 229 | + |
| 230 | +```json |
| 231 | +{ |
| 232 | + "@typescript-eslint/naming-conventions": [ |
| 233 | + "error", |
| 234 | + { "selector": "variableLike", "format": ["camelCase"] } |
| 235 | + ] |
| 236 | +} |
| 237 | +``` |
| 238 | + |
| 239 | +### Enforce that private members are prefixed with an underscore |
| 240 | + |
| 241 | +```json |
| 242 | +{ |
| 243 | + "@typescript-eslint/naming-conventions": [ |
| 244 | + "error", |
| 245 | + { |
| 246 | + "selector": "memberLike", |
| 247 | + "modifier": ["private"], |
| 248 | + "format": ["camelCase"], |
| 249 | + "leadingUnderscore": "require" |
| 250 | + } |
| 251 | + ] |
| 252 | +} |
| 253 | +``` |
| 254 | + |
| 255 | +### Enforce that boolean variables are prefixed with an allowed verb |
| 256 | + |
| 257 | +```json |
| 258 | +{ |
| 259 | + "@typescript-eslint/naming-conventions": [ |
| 260 | + "error", |
| 261 | + { |
| 262 | + "selector": "variable", |
| 263 | + "types": ["boolean"], |
| 264 | + "format": ["PascalCase"], |
| 265 | + "prefix": ["is", "should", "has", "can", "did", "will"] |
| 266 | + } |
| 267 | + ] |
| 268 | +} |
| 269 | +``` |
| 270 | + |
| 271 | +### Enforce that all variables are either in camelCase or UPPER_CASE |
| 272 | + |
| 273 | +```json |
| 274 | +{ |
| 275 | + "@typescript-eslint/naming-conventions": [ |
| 276 | + "error", |
| 277 | + { |
| 278 | + "selector": "variable", |
| 279 | + "format": ["camelCase", "UPPER_CASE"] |
| 280 | + } |
| 281 | + ] |
| 282 | +} |
| 283 | +``` |
| 284 | + |
| 285 | +### Enforce that type parameters (generics) are prefixed with `T` |
| 286 | + |
| 287 | +```json |
| 288 | +{ |
| 289 | + "@typescript-eslint/naming-conventions": [ |
| 290 | + "error", |
| 291 | + { |
| 292 | + "selector": "typeParameter", |
| 293 | + "format": ["PascalCase"], |
| 294 | + "prefix": ["T"] |
| 295 | + } |
| 296 | + ] |
| 297 | +} |
| 298 | +``` |
| 299 | + |
| 300 | +### Enforce the codebase follows ESLint's `camelcase` conventions |
| 301 | + |
| 302 | +```json |
| 303 | +{ |
| 304 | + "@typescript-eslint/naming-conventions": [ |
| 305 | + "error", |
| 306 | + { |
| 307 | + "selector": "default", |
| 308 | + "format": ["camelCase"] |
| 309 | + }, |
| 310 | + |
| 311 | + { |
| 312 | + "selector": "variable", |
| 313 | + "format": ["camelCase", "UPPER_CASE"] |
| 314 | + }, |
| 315 | + { |
| 316 | + "selector": "parameter", |
| 317 | + "format": ["camelCase"], |
| 318 | + "leadingUnderscore": "allow" |
| 319 | + }, |
| 320 | + |
| 321 | + { |
| 322 | + "selector": "memberLike", |
| 323 | + "modifiers": ["private"], |
| 324 | + "format": ["camelCase"], |
| 325 | + "leadingUnderscore": "require" |
| 326 | + }, |
| 327 | + |
| 328 | + { |
| 329 | + "selector": "typeLike", |
| 330 | + "format": ["PascalCase"] |
| 331 | + } |
| 332 | + ] |
| 333 | +} |
| 334 | +``` |
| 335 | + |
| 336 | +## When Not To Use It |
| 337 | + |
| 338 | +If you do not want to enforce naming conventions for anything. |
0 commit comments