From 4e2694b6b7436eae2d133d6fb714338b7fe91fe2 Mon Sep 17 00:00:00 2001 From: Olivier Zalmanski <88216225+OlivierZal@users.noreply.github.com> Date: Thu, 27 Mar 2025 14:08:01 +0100 Subject: [PATCH 1/3] feat(eslint-plugin): [prefer-nullish-coalescing] create `ignoreIfStatements` option --- .../docs/rules/prefer-nullish-coalescing.mdx | 49 +++++++++++++++ .../src/rules/prefer-nullish-coalescing.ts | 10 +++- .../prefer-nullish-coalescing.shot | 60 ++++++++++++++++--- .../rules/prefer-nullish-coalescing.test.ts | 24 ++++++++ 4 files changed, 135 insertions(+), 8 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.mdx b/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.mdx index 904c7f40b7fe..90c6094c38bb 100644 --- a/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.mdx +++ b/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.mdx @@ -118,6 +118,55 @@ 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 lazyInitializeFoo() { + if (!foo) { + foo = makeFoo(); + } +} + +declare let bar: { a: string } | null; +declare function makeBar(): { a: string }; + +function lazyInitializeBar() { + if (!bar) bar = makeBar(); +} +``` + + + + +```ts option='{ "ignoreIfStatements": false }' +declare let foo: { a: string } | null; +declare function makeFoo(): { a: string }; + +function lazyInitializeFoo() { + foo ??= makeFoo(); +} + +declare let bar: { a: string } | null; +declare function makeBar(): { a: string }; + +function lazyInitializeBar() { + bar ??= makeBar(); +} +``` + + + + ### `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..940566a801a8 100644 --- a/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts +++ b/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts @@ -50,6 +50,7 @@ export type Options = [ } | true; ignoreTernaryTests?: boolean; + ignoreIfStatements?: boolean; }, ]; @@ -145,6 +146,11 @@ export default createRule({ description: 'Whether to ignore any ternary expressions that could be simplified by using the nullish coalescing operator.', }, + ignoreIfStatements: { + type: 'boolean', + description: + 'Whether to ignore any if statements that could be simplified by using the nullish coalescing operator.', + }, }, }, ], @@ -162,6 +168,7 @@ export default createRule({ string: false, }, ignoreTernaryTests: false, + ignoreIfStatements: false, }, ], create( @@ -174,6 +181,7 @@ export default createRule({ ignoreMixedLogicalExpressions, ignorePrimitives, ignoreTernaryTests, + ignoreIfStatements, }, ], ) { @@ -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..33092a4f6852 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,52 @@ 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 lazyInitializeFoo() { + if (!foo) { + ~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??=\`) instead of an assignment expression, as it is simpler to read. + foo = makeFoo(); +~~~~~~~~~~~~~~~~~~~~ + } +~~~ +} + +declare let bar: { a: string } | null; +declare function makeBar(): { a: string }; + +function lazyInitializeBar() { + if (!bar) bar = makeBar(); + ~~~~~~~~~~~~~~~~~~~~~~~~~~ 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 lazyInitializeFoo() { + foo ??= makeFoo(); +} + +declare let bar: { a: string } | null; +declare function makeBar(): { a: string }; + +function lazyInitializeBar() { + bar ??= makeBar(); +} +" +`; + +exports[`Validating rule docs prefer-nullish-coalescing.mdx code examples ESLint output 7`] = ` +"Incorrect Options: { "ignoreConditionalTests": false } declare let a: string | null; @@ -126,7 +172,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 +191,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 +215,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 +232,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 +243,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 +253,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 +265,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) => ({ From 221e8667be3ac092fd7f4a2013224c011cb6bd2d Mon Sep 17 00:00:00 2001 From: Olivier Zalmanski <88216225+OlivierZal@users.noreply.github.com> Date: Thu, 27 Mar 2025 14:49:21 +0100 Subject: [PATCH 2/3] sorting --- .../src/rules/prefer-nullish-coalescing.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts b/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts index 940566a801a8..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?: | { @@ -50,7 +51,6 @@ export type Options = [ } | true; ignoreTernaryTests?: boolean; - ignoreIfStatements?: boolean; }, ]; @@ -103,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: @@ -146,11 +151,6 @@ export default createRule({ description: 'Whether to ignore any ternary expressions that could be simplified by using the nullish coalescing operator.', }, - ignoreIfStatements: { - type: 'boolean', - description: - 'Whether to ignore any if statements that could be simplified by using the nullish coalescing operator.', - }, }, }, ], @@ -160,6 +160,7 @@ export default createRule({ allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: false, ignoreBooleanCoercion: false, ignoreConditionalTests: true, + ignoreIfStatements: false, ignoreMixedLogicalExpressions: false, ignorePrimitives: { bigint: false, @@ -168,7 +169,6 @@ export default createRule({ string: false, }, ignoreTernaryTests: false, - ignoreIfStatements: false, }, ], create( @@ -178,10 +178,10 @@ export default createRule({ allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing, ignoreBooleanCoercion, ignoreConditionalTests, + ignoreIfStatements, ignoreMixedLogicalExpressions, ignorePrimitives, ignoreTernaryTests, - ignoreIfStatements, }, ], ) { From 74b5b47f29492401314cc889eb35e5ea6d1308d9 Mon Sep 17 00:00:00 2001 From: Olivier Zalmanski <88216225+OlivierZal@users.noreply.github.com> Date: Thu, 27 Mar 2025 16:30:05 +0100 Subject: [PATCH 3/3] update snapshots --- .../docs/rules/prefer-nullish-coalescing.mdx | 18 ++++++------------ .../prefer-nullish-coalescing.shot | 18 ++++++------------ .../prefer-nullish-coalescing.shot | 6 ++++++ 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.mdx b/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.mdx index 90c6094c38bb..5a99fe0259c6 100644 --- a/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.mdx +++ b/packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.mdx @@ -131,17 +131,14 @@ Examples of code for this rule with `{ ignoreIfStatements: false }`: declare let foo: { a: string } | null; declare function makeFoo(): { a: string }; -function lazyInitializeFoo() { +function lazyInitializeFoo1() { if (!foo) { foo = makeFoo(); } } -declare let bar: { a: string } | null; -declare function makeBar(): { a: string }; - -function lazyInitializeBar() { - if (!bar) bar = makeBar(); +function lazyInitializeFoo2() { + if (!foo) foo = makeFoo(); } ``` @@ -152,15 +149,12 @@ function lazyInitializeBar() { declare let foo: { a: string } | null; declare function makeFoo(): { a: string }; -function lazyInitializeFoo() { +function lazyInitializeFoo1() { foo ??= makeFoo(); } -declare let bar: { a: string } | null; -declare function makeBar(): { a: string }; - -function lazyInitializeBar() { - bar ??= makeBar(); +function lazyInitializeFoo2() { + foo ??= makeFoo(); } ``` 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 33092a4f6852..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 @@ -107,7 +107,7 @@ Options: { "ignoreIfStatements": false } declare let foo: { a: string } | null; declare function makeFoo(): { a: string }; -function lazyInitializeFoo() { +function lazyInitializeFoo1() { if (!foo) { ~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??=\`) instead of an assignment expression, as it is simpler to read. foo = makeFoo(); @@ -116,11 +116,8 @@ function lazyInitializeFoo() { ~~~ } -declare let bar: { a: string } | null; -declare function makeBar(): { a: string }; - -function lazyInitializeBar() { - if (!bar) bar = makeBar(); +function lazyInitializeFoo2() { + if (!foo) foo = makeFoo(); ~~~~~~~~~~~~~~~~~~~~~~~~~~ Prefer using nullish coalescing operator (\`??=\`) instead of an assignment expression, as it is simpler to read. } " @@ -133,15 +130,12 @@ Options: { "ignoreIfStatements": false } declare let foo: { a: string } | null; declare function makeFoo(): { a: string }; -function lazyInitializeFoo() { +function lazyInitializeFoo1() { foo ??= makeFoo(); } -declare let bar: { a: string } | null; -declare function makeBar(): { a: string }; - -function lazyInitializeBar() { - bar ??= makeBar(); +function lazyInitializeFoo2() { + foo ??= makeFoo(); } " `; 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. */