diff --git a/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.mdx b/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.mdx index 904c7f40b7fe..5a99fe0259c6 100644 --- a/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.mdx +++ b/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.mdx @@ -118,6 +118,49 @@ c ?? 'a string'; +### `ignoreIfStatements` + +{/* insert option description */} + +Examples of code for this rule with `{ ignoreIfStatements: false }`: + + + + +```ts option='{ "ignoreIfStatements": false }' +declare let foo: { a: string } | null; +declare function makeFoo(): { a: string }; + +function lazyInitializeFoo1() { + if (!foo) { + foo = makeFoo(); + } +} + +function lazyInitializeFoo2() { + if (!foo) foo = makeFoo(); +} +``` + + + + +```ts option='{ "ignoreIfStatements": false }' +declare let foo: { a: string } | null; +declare function makeFoo(): { a: string }; + +function lazyInitializeFoo1() { + foo ??= makeFoo(); +} + +function lazyInitializeFoo2() { + foo ??= makeFoo(); +} +``` + + + + ### `ignoreConditionalTests` {/* insert option description */} diff --git a/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts b/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts index 8f49f74089a8..b05e43d0431e 100644 --- a/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts +++ b/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts @@ -40,6 +40,7 @@ export type Options = [ allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing?: boolean; ignoreBooleanCoercion?: boolean; ignoreConditionalTests?: boolean; + ignoreIfStatements?: boolean; ignoreMixedLogicalExpressions?: boolean; ignorePrimitives?: | { @@ -102,6 +103,11 @@ export default createRule({ description: 'Whether to ignore cases that are located within a conditional test.', }, + ignoreIfStatements: { + type: 'boolean', + description: + 'Whether to ignore any if statements that could be simplified by using the nullish coalescing operator.', + }, ignoreMixedLogicalExpressions: { type: 'boolean', description: @@ -154,6 +160,7 @@ export default createRule({ allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: false, ignoreBooleanCoercion: false, ignoreConditionalTests: true, + ignoreIfStatements: false, ignoreMixedLogicalExpressions: false, ignorePrimitives: { bigint: false, @@ -171,6 +178,7 @@ export default createRule({ allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing, ignoreBooleanCoercion, ignoreConditionalTests, + ignoreIfStatements, ignoreMixedLogicalExpressions, ignorePrimitives, ignoreTernaryTests, @@ -516,7 +524,7 @@ export default createRule({ } }, IfStatement(node: TSESTree.IfStatement): void { - if (node.alternate != null) { + if (ignoreIfStatements || node.alternate != null) { return; } 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 46ff002cd623..b3d8543af57d 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 @@ -102,6 +102,46 @@ c ?? 'a string'; exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 5`] = ` "Incorrect +Options: { "ignoreIfStatements": false } + +declare let foo: { a: string } | null; +declare function makeFoo(): { a: string }; + +function lazyInitializeFoo1() { + if (!foo) { + ~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??=\`) instead of an assignment expression, as it is simpler to read. + foo = makeFoo(); +~~~~~~~~~~~~~~~~~~~~ + } +~~~ +} + +function lazyInitializeFoo2() { + if (!foo) foo = makeFoo(); + ~~~~~~~~~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??=\`) instead of an assignment expression, as it is simpler to read. +} +" +`; + +exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 6`] = ` +"Correct +Options: { "ignoreIfStatements": false } + +declare let foo: { a: string } | null; +declare function makeFoo(): { a: string }; + +function lazyInitializeFoo1() { + foo ??= makeFoo(); +} + +function lazyInitializeFoo2() { + foo ??= makeFoo(); +} +" +`; + +exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 7`] = ` +"Incorrect Options: { "ignoreConditionalTests": false } declare let a: string | null; @@ -126,7 +166,7 @@ a || b ? true : false; " `; -exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 6`] = ` +exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 8`] = ` "Correct Options: { "ignoreConditionalTests": false } @@ -145,7 +185,7 @@ for (let i = 0; a ?? b; i += 1) {} " `; -exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 7`] = ` +exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 9`] = ` "Incorrect Options: { "ignoreMixedLogicalExpressions": false } @@ -169,7 +209,7 @@ a || (b && c && d); " `; -exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 8`] = ` +exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 10`] = ` "Correct Options: { "ignoreMixedLogicalExpressions": false } @@ -186,7 +226,7 @@ a ?? (b && c && d); " `; -exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 9`] = ` +exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 11`] = ` "Incorrect Options: { "ignorePrimitives": { "string": false } } @@ -197,7 +237,7 @@ foo || 'a string'; " `; -exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 10`] = ` +exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 12`] = ` "Correct Options: { "ignorePrimitives": { "string": false } } @@ -207,7 +247,7 @@ foo ?? 'a string'; " `; -exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 11`] = ` +exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 13`] = ` "Incorrect Options: { "ignoreBooleanCoercion": false } @@ -219,7 +259,7 @@ const x = Boolean(a || b); " `; -exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 12`] = ` +exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 14`] = ` "Correct Options: { "ignoreBooleanCoercion": false } 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 b17d4753f074..0926a839ee38 100644 --- a/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts @@ -518,6 +518,30 @@ x?.a ? y?.a : 'foo' code, options: [{ ignoreTernaryTests: false }] as const, })), + { + code: ` +declare let foo: { a: string } | null; +declare function makeFoo(): { a: string }; + +function lazyInitialize() { + if (!foo) { + foo = makeFoo(); + } +} + `, + options: [{ ignoreIfStatements: true }], + }, + { + code: ` +declare let foo: { a: string } | null; +declare function makeFoo(): { a: string }; + +function lazyInitialize() { + if (!foo) foo = makeFoo(); +} + `, + options: [{ ignoreIfStatements: true }], + }, // ignoreConditionalTests ...nullishTypeTest((nullish, type, equals) => ({ diff --git a/packages/eslint-plugin/tests/schema-snapshots/prefer-nullish-coalescing.shot b/packages/eslint-plugin/tests/schema-snapshots/prefer-nullish-coalescing.shot index ea441036f3a0..7c3b288ca1b4 100644 --- a/packages/eslint-plugin/tests/schema-snapshots/prefer-nullish-coalescing.shot +++ b/packages/eslint-plugin/tests/schema-snapshots/prefer-nullish-coalescing.shot @@ -20,6 +20,10 @@ exports[`Rule schemas should be convertible to TS types for documentation purpos "description": "Whether to ignore cases that are located within a conditional test.", "type": "boolean" }, + "ignoreIfStatements": { + "description": "Whether to ignore any if statements that could be simplified by using the nullish coalescing operator.", + "type": "boolean" + }, "ignoreMixedLogicalExpressions": { "description": "Whether to ignore any logical or expressions that are part of a mixed logical expression (with \`&&\`).", "type": "boolean" @@ -76,6 +80,8 @@ type Options = [ ignoreBooleanCoercion?: boolean; /** Whether to ignore cases that are located within a conditional test. */ ignoreConditionalTests?: boolean; + /** Whether to ignore any if statements that could be simplified by using the nullish coalescing operator. */ + ignoreIfStatements?: boolean; /** Whether to ignore any logical or expressions that are part of a mixed logical expression (with \`&&\`). */ ignoreMixedLogicalExpressions?: boolean; /** Whether to ignore all (\`true\`) or some (an object with properties) primitive types. */