diff --git a/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.mdx b/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.mdx index 2de5f81c71d7..87cf88527046 100644 --- a/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.mdx +++ b/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.mdx @@ -28,46 +28,48 @@ This rule will not work as expected if [`strictNullChecks`](https://www.typescri {/* insert option description */} -Incorrect code for `ignoreTernaryTests: false`, and correct code for `ignoreTernaryTests: true`: - -```ts option='{ "ignoreTernaryTests": false }' showPlaygroundButton -const foo: any = 'bar'; -foo !== undefined && foo !== null ? foo : 'a string'; -foo === undefined || foo === null ? 'a string' : foo; -foo == undefined ? 'a string' : foo; -foo == null ? 'a string' : foo; - -const foo: string | undefined = 'bar'; -foo !== undefined ? foo : 'a string'; -foo === undefined ? 'a string' : foo; - -const foo: string | null = 'bar'; -foo !== null ? foo : 'a string'; -foo ? foo : 'a string'; -foo === null ? 'a string' : foo; -!foo ? 'a string' : foo; +Examples of code for this rule with `{ ignoreTernaryTests: false }`: + + + + +```ts option='{ "ignoreTernaryTests": false }' +declare const a: any; +a !== undefined && a !== null ? a : 'a string'; +a === undefined || a === null ? 'a string' : a; +a == undefined ? 'a string' : a; +a == null ? 'a string' : a; + +declare const b: string | undefined; +b !== undefined ? b : 'a string'; +b === undefined ? 'a string' : b; +b ? b : 'a string'; +!b ? 'a string' : b; + +declare const c: string | null; +c !== null ? c : 'a string'; +c === null ? 'a string' : c; +c ? c : 'a string'; +!c ? 'a string' : c; ``` -Correct code for `ignoreTernaryTests: false`: + + -```ts option='{ "ignoreTernaryTests": false }' showPlaygroundButton -const foo: any = 'bar'; -foo ?? 'a string'; -foo ?? 'a string'; -foo ?? 'a string'; -foo ?? 'a string'; +```ts option='{ "ignoreTernaryTests": false }' +declare const a: any; +a ?? 'a string'; -const foo: string | undefined = 'bar'; -foo ?? 'a string'; -foo ?? 'a string'; +declare const b: string | undefined; +b ?? 'a string'; -const foo: string | null = 'bar'; -foo ?? 'a string'; -foo ?? 'a string'; -foo ?? 'a string'; -foo ?? 'a string'; +declare const c: string | null; +c ?? 'a string'; ``` + + + ### `ignoreConditionalTests` {/* insert option description */} @@ -76,9 +78,12 @@ Generally expressions within conditional tests intentionally use the falsy fallt If you're looking to enforce stricter conditional tests, you should consider using the `strict-boolean-expressions` rule. -Incorrect code for `ignoreConditionalTests: false`, and correct code for `ignoreConditionalTests: true`: +Examples of code for this rule with `{ ignoreConditionalTests: false }`: + + + -```ts option='{ "ignoreConditionalTests": false }' showPlaygroundButton +```ts option='{ "ignoreConditionalTests": false }' declare const a: string | null; declare const b: string | null; @@ -93,9 +98,10 @@ for (let i = 0; a || b; i += 1) {} a || b ? true : false; ``` -Correct code for `ignoreConditionalTests: false`: + + -```ts option='{ "ignoreConditionalTests": false }' showPlaygroundButton +```ts option='{ "ignoreConditionalTests": false }' declare const a: string | null; declare const b: string | null; @@ -110,6 +116,9 @@ for (let i = 0; a ?? b; i += 1) {} (a ?? b) ? true : false; ``` + + + ### `ignoreMixedLogicalExpressions` {/* insert option description */} @@ -118,9 +127,12 @@ Generally expressions within mixed logical expressions intentionally use the fal If you're looking to enforce stricter conditional tests, you should consider using the `strict-boolean-expressions` rule. -Incorrect code for `ignoreMixedLogicalExpressions: false`, and correct code for `ignoreMixedLogicalExpressions: true`: +Examples of code for this rule with `{ ignoreMixedLogicalExpressions: false }`: + + + -```ts option='{ "ignoreMixedLogicalExpressions": false }' showPlaygroundButton +```ts option='{ "ignoreMixedLogicalExpressions": false }' declare const a: string | null; declare const b: string | null; declare const c: string | null; @@ -133,9 +145,10 @@ a || (b && c) || d; a || (b && c && d); ``` -Correct code for `ignoreMixedLogicalExpressions: false`: + + -```ts option='{ "ignoreMixedLogicalExpressions": false }' showPlaygroundButton +```ts option='{ "ignoreMixedLogicalExpressions": false }' declare const a: string | null; declare const b: string | null; declare const c: string | null; @@ -148,6 +161,9 @@ a ?? (b && c) ?? d; a ?? (b && c && d); ``` + + + **_NOTE:_** Errors for this specific case will be presented as suggestions (see below), instead of fixes. This is because it is not always safe to automatically convert `||` to `??` within a mixed logical expression, as we cannot tell the intended precedence of the operator. Note that by design, `??` requires parentheses when used with `&&` or `||` in the same expression. ### `ignorePrimitives` @@ -156,49 +172,65 @@ a ?? (b && c && d); If you would like to ignore expressions containing operands of certain primitive types that can be falsy then you may pass an object containing a boolean value for each primitive: -- `string: true`, ignores `null` or `undefined` unions with `string` (default: false). -- `number: true`, ignores `null` or `undefined` unions with `number` (default: false). -- `bigint: true`, ignores `null` or `undefined` unions with `bigint` (default: false). -- `boolean: true`, ignores `null` or `undefined` unions with `boolean` (default: false). +- `string: true`, ignores `null` or `undefined` unions with `string` (default: `false`). +- `number: true`, ignores `null` or `undefined` unions with `number` (default: `false`). +- `bigint: true`, ignores `null` or `undefined` unions with `bigint` (default: `false`). +- `boolean: true`, ignores `null` or `undefined` unions with `boolean` (default: `false`). -Incorrect code for `ignorePrimitives: { string: false }`, and correct code for `ignorePrimitives: { string: true }`: +Examples of code for this rule with `{ ignorePrimitives: { string: false } }`: + + + + +```ts option='{ "ignorePrimitives": { "string": false } }' +declare const foo: string | undefined; -```ts option='{ "ignorePrimitives": { "string": true } }' showPlaygroundButton -const foo: string | undefined = 'bar'; foo || 'a string'; ``` -Correct code for both `ignorePrimitives: { string: false }` and `ignorePrimitives: { string: true }`: + + + +```ts option='{ "ignorePrimitives": { "string": false } }' +declare const foo: string | undefined; -```ts option='{ "ignorePrimitives": { "string": true } }' showPlaygroundButton -const foo: string | undefined = 'bar'; foo ?? 'a string'; ``` + + + Also, if you would like to ignore all primitives types, you can set `ignorePrimitives: true`. It is equivalent to `ignorePrimitives: { string: true, number: true, bigint: true, boolean: true }`. ### `ignoreBooleanCoercion` {/* insert option description */} -Incorrect code for `ignoreBooleanCoercion: false`, and correct code for `ignoreBooleanCoercion: true`: +Examples of code for this rule with `{ ignoreBooleanCoercion: false }`: + + + -```ts option='{ "ignoreBooleanCoercion": true }' showPlaygroundButton -let a: string | true | undefined; -let b: string | boolean | undefined; +```ts option='{ "ignoreBooleanCoercion": false }' +declare const a: string | true | undefined; +declare const b: string | boolean | undefined; const x = Boolean(a || b); ``` -Correct code for `ignoreBooleanCoercion: false`: + + -```ts option='{ "ignoreBooleanCoercion": false }' showPlaygroundButton -let a: string | true | undefined; -let b: string | boolean | undefined; +```ts option='{ "ignoreBooleanCoercion": false }' +declare const a: string | true | undefined; +declare const b: string | boolean | undefined; const x = Boolean(a ?? b); ``` + + + ### `allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing` :::danger Deprecated diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-nullish-coalescing.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-nullish-coalescing.shot index aeaefe9dc5ff..124ed091013c 100644 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-nullish-coalescing.shot +++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-nullish-coalescing.shot @@ -1,53 +1,59 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 1`] = ` -"Options: { "ignoreTernaryTests": false } - -const foo: any = 'bar'; -foo !== undefined && foo !== null ? foo : 'a string'; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??\`) instead of a ternary expression, as it is simpler to read. -foo === undefined || foo === null ? 'a string' : foo; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??\`) instead of a ternary expression, as it is simpler to read. -foo == undefined ? 'a string' : foo; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??\`) instead of a ternary expression, as it is simpler to read. -foo == null ? 'a string' : foo; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??\`) instead of a ternary expression, as it is simpler to read. - -const foo: string | undefined = 'bar'; -foo !== undefined ? foo : 'a string'; -foo === undefined ? 'a string' : foo; - -const foo: string | null = 'bar'; -foo !== null ? foo : 'a string'; -foo ? foo : 'a string'; -foo === null ? 'a string' : foo; -!foo ? 'a string' : foo; +"Incorrect +Options: { "ignoreTernaryTests": false } + +declare const a: any; +a !== undefined && a !== null ? a : 'a string'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??\`) instead of a ternary expression, as it is simpler to read. +a === undefined || a === null ? 'a string' : a; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??\`) instead of a ternary expression, as it is simpler to read. +a == undefined ? 'a string' : a; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??\`) instead of a ternary expression, as it is simpler to read. +a == null ? 'a string' : a; +~~~~~~~~~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??\`) instead of a ternary expression, as it is simpler to read. + +declare const b: string | undefined; +b !== undefined ? b : 'a string'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??\`) instead of a ternary expression, as it is simpler to read. +b === undefined ? 'a string' : b; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??\`) instead of a ternary expression, as it is simpler to read. +b ? b : 'a string'; +~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??\`) instead of a ternary expression, as it is simpler to read. +!b ? 'a string' : b; +~~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??\`) instead of a ternary expression, as it is simpler to read. + +declare const c: string | null; +c !== null ? c : 'a string'; +~~~~~~~~~~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??\`) instead of a ternary expression, as it is simpler to read. +c === null ? 'a string' : c; +~~~~~~~~~~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??\`) instead of a ternary expression, as it is simpler to read. +c ? c : 'a string'; +~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??\`) instead of a ternary expression, as it is simpler to read. +!c ? 'a string' : c; +~~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??\`) instead of a ternary expression, as it is simpler to read. " `; exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 2`] = ` -"Options: { "ignoreTernaryTests": false } +"Correct +Options: { "ignoreTernaryTests": false } -const foo: any = 'bar'; -foo ?? 'a string'; -foo ?? 'a string'; -foo ?? 'a string'; -foo ?? 'a string'; +declare const a: any; +a ?? 'a string'; -const foo: string | undefined = 'bar'; -foo ?? 'a string'; -foo ?? 'a string'; +declare const b: string | undefined; +b ?? 'a string'; -const foo: string | null = 'bar'; -foo ?? 'a string'; -foo ?? 'a string'; -foo ?? 'a string'; -foo ?? 'a string'; +declare const c: string | null; +c ?? 'a string'; " `; exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 3`] = ` -"Options: { "ignoreConditionalTests": false } +"Incorrect +Options: { "ignoreConditionalTests": false } declare const a: string | null; declare const b: string | null; @@ -70,7 +76,8 @@ a || b ? true : false; `; exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 4`] = ` -"Options: { "ignoreConditionalTests": false } +"Correct +Options: { "ignoreConditionalTests": false } declare const a: string | null; declare const b: string | null; @@ -88,7 +95,8 @@ for (let i = 0; a ?? b; i += 1) {} `; exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 5`] = ` -"Options: { "ignoreMixedLogicalExpressions": false } +"Incorrect +Options: { "ignoreMixedLogicalExpressions": false } declare const a: string | null; declare const b: string | null; @@ -110,7 +118,8 @@ a || (b && c && d); `; exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 6`] = ` -"Options: { "ignoreMixedLogicalExpressions": false } +"Correct +Options: { "ignoreMixedLogicalExpressions": false } declare const a: string | null; declare const b: string | null; @@ -126,36 +135,44 @@ a ?? (b && c && d); `; exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 7`] = ` -"Options: { "ignorePrimitives": { "string": true } } +"Incorrect +Options: { "ignorePrimitives": { "string": false } } + +declare const foo: string | undefined; -const foo: string | undefined = 'bar'; foo || 'a string'; + ~~ Prefer using nullish coalescing operator (\`??\`) instead of a logical or (\`||\`), as it is a safer operator. " `; exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 8`] = ` -"Options: { "ignorePrimitives": { "string": true } } +"Correct +Options: { "ignorePrimitives": { "string": false } } + +declare const foo: string | undefined; -const foo: string | undefined = 'bar'; foo ?? 'a string'; " `; exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 9`] = ` -"Options: { "ignoreBooleanCoercion": true } +"Incorrect +Options: { "ignoreBooleanCoercion": false } -let a: string | true | undefined; -let b: string | boolean | undefined; +declare const a: string | true | undefined; +declare const b: string | boolean | undefined; const x = Boolean(a || b); + ~~ Prefer using nullish coalescing operator (\`??\`) instead of a logical or (\`||\`), as it is a safer operator. " `; exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 10`] = ` -"Options: { "ignoreBooleanCoercion": false } +"Correct +Options: { "ignoreBooleanCoercion": false } -let a: string | true | undefined; -let b: string | boolean | undefined; +declare const a: string | true | undefined; +declare const b: string | boolean | undefined; const x = Boolean(a ?? b); " diff --git a/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts b/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts index a9ad05c5cef5..99cca5c7369c 100644 --- a/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts @@ -154,11 +154,11 @@ declare let x: string | undefined | null; x !== null ? x : y; `, ` -declare let x: string | null | any; +declare let x: any; x === null ? x : y; `, ` -declare let x: string | null | unknown; +declare let x: unknown; x === null ? x : y; `, `