# @rushstack/eslint-plugin
This plugin implements supplementary rules for use with the `@rushstack/eslint-config` package,
which provides a TypeScript ESLint ruleset tailored for large teams and projects.
Please see [that project's documentation](https://www.npmjs.com/package/@rushstack/eslint-config)
for details. To learn about Rush Stack, please visit: [https://rushstack.io/](https://rushstack.io/)
## `@rushstack/hoist-jest-mock`
Require Jest module mocking APIs to be called before any other statements in their code block.
#### Rule Details
Jest module mocking APIs such as "jest.mock()" must be called before the associated module is imported, otherwise
they will have no effect. Transpilers such as `ts-jest` and `babel-jest` automatically "hoist" these calls, however
this can produce counterintuitive behavior. Instead, the `hoist-jest-mocks` lint rule simply requires developers
to write the statements in the correct order.
The following APIs are affected: 'jest.mock()', 'jest.unmock()', 'jest.enableAutomock()', 'jest.disableAutomock()',
'jest.deepUnmock()'.
For technical background, please read the Jest documentation here: https://jestjs.io/docs/en/es6-class-mocks
#### Examples
The following patterns are considered problems when `@rushstack/hoist-jest-mock` is enabled:
```ts
import * as file from './file'; // import statement
jest.mock('./file'); // error
test("example", () => {
jest.mock('./file2'); // error
});
```
```ts
require('./file'); // import statement
jest.mock('./file'); // error
```
The following patterns are NOT considered problems:
```ts
jest.mock('./file'); // okay, because mock() precedes the import below
import * as file from './file'; // import statement
```
```ts
// These statements are not real "imports" because they import compile-time types
// without any runtime effects
import type { X } from './file';
let y: typeof import('./file');
jest.mock('./file'); // okay
```
## `@rushstack/no-new-null`
Prevent usage of the JavaScript `null` value, while allowing code to access existing APIs that
may require `null`.
#### Rule Details
Most programming languages have a "null" or "nil" value that serves several purposes:
1. the initial value for an uninitialized variable
2. the value of `x.y` or `x["y"]` when `x` has no such key, and
3. a special token that developers can assign to indicate an unknown or empty state.
In JavaScript, the `undefined` value fulfills all three roles. JavaScript's `null` value is a redundant secondary
token that only fulfills (3), even though its name confusingly implies otherwise. The `null` value was arguably
a mistake in the original JavaScript language design, but it cannot be banned entirely because it is returned
by some entrenched system APIs such as `JSON.parse()`, and also some popular NPM packages. Thus, this rule aims
to tolerate preexisting `null` values while preventing new ones from being introduced.
The `@rushstack/no-new-null` rule flags type definitions with `null` that can be exported or used by others.
The rule ignores declarations that are local variables, private members, or types that are not exported.
If you are designing a new JSON file format, it's a good idea to avoid `null` entirely. In most cases
there are better representations that convey more information about an item that is unknown, omitted,
or disabled. If you do need to declare types for JSON structures containing `null`, rather than
suppressing the lint rule, you can use a specialized
[JsonNull](https://rushstack.io/pages/api/node-core-library.jsonnull/)
type as provided by [@rushstack/node-core-library](https://www.npmjs.com/package/@rushstack/node-core-library).
#### Examples
The following patterns are considered problems when `@rushstack/no-new-null` is enabled:
```ts
// interface declaration with null field
interface IHello { hello: null; } // error
// type declaration with null field
type Hello = { hello: null; } // error
// type function alias
type T = (args: string | null) => void; // error
// type alias
type N = null; // error
// type constructor
type C = {new (args: string | null)} // error
// function declaration with null args
function hello(world: string | null): void {}; // error
function legacy(callback: (err: Error| null) => void): void { }; // error
// function with null return type
function hello(): (err: Error | null) => void {}; // error
// const with null type
const nullType: 'hello' | null = 'hello'; // error
// classes with publicly visible properties and methods
class PublicNulls {
property: string | null; // error
propertyFunc: (val: string | null) => void; // error
legacyImplicitPublic(hello: string | null): void {} // error
public legacyExplicitPublic(hello: string | null): void {} // error
}
```
The following patterns are NOT considered problems:
```ts
// wrapping an null-API
export function ok(hello: string): void {
const innerCallback: (err: Error | null) => void = (e) => {}; // passes
return innerCallback(null);
}
// classes where null APIs are used, but are private-only
class PrivateNulls {
private pField: string | null; // passes
private pFunc: (val: string | null) => void; // passes
private legacyPrivate(hello: string | null): void { // passes
this.pField = hello;
this.pFunc(this.pField)
this.pFunc('hello')
}
}
```
## `@rushstack/no-null`
(Deprecated) Prevent usage of JavaScript's `null` keyword.
#### Rule Details
This rule has been superseded by `@rushstack/no-new-null`, and is maintained to support code that has not
migrated to the new rule yet. The `@rushstack/no-null` rule prohibits `null` as a literal value, but allows
it in type annotations. Comparisons with `null` are also allowed.
#### Examples
The following patterns are considered problems when `@rushstack/no-null` is enabled:
```ts
let x = null; // error
f(null); // error
function g() {
return null; // error
}
```
The following patterns are NOT considered problems:
```ts
let x: number | null = f(); // declaring types as possibly "null" is okay
if (x === null) { // comparisons are okay
x = 0;
}
```
## `@rushstack/no-untyped-underscore` (Opt-in)
Prevent TypeScript code from accessing legacy JavaScript members whose name has an underscore prefix.
#### Rule Details
JavaScript does not provide a straightforward way to restrict access to object members, so API names commonly
indicate a private member by using an underscore prefix (e.g. `exampleObject._privateMember`). For inexperienced
developers who may be unfamiliar with this convention, in TypeScript we can mark the APIs as `private` or omit them
from the typings. However, when migrating a large code base to TypeScript, it may be difficult to declare types
for every legacy API. In this situation, the `@rushstack/no-untyped-underscore` rule can help.
This rule detects expressions that access a member with an underscore prefix, EXCEPT in cases where:
- The object is typed: specifically, `exampleObject` has a TypeScript type that declares `_privateMember`; OR
- The object expression uses: the `this` or `super` keywords; OR
- The object expression is a variable named `that`. (In older ES5 code, `that` was commonly used as an alias
for `this` in unbound contexts.)
#### Examples
The following patterns are considered problems when `@rushstack/no-untyped-underscore` is enabled:
```ts
let x: any;
x._privateMember = 123; // error, because x is untyped
let x: { [key: string]: number };
x._privateMember = 123; // error, because _privateMember is not a declared member of x's type
```
The following patterns are NOT considered problems:
```ts
let x: { _privateMember: any };
x._privateMember = 123; // okay, because _privateMember is declared by x's type
let x = { _privateMember: 0 };
x._privateMember = 123; // okay, because _privateMember is part of the inferred type
enum E {
_PrivateMember
}
let e: E._PrivateMember = E._PrivateMember; // okay, because _PrivateMember is declared by E
```
## Links
- [CHANGELOG.md](
https://github.com/microsoft/rushstack/blob/main/stack/eslint-plugin/CHANGELOG.md) - Find
out what's new in the latest version
`@rushstack/eslint-plugin` is part of the [Rush Stack](https://rushstack.io/) family of projects.