From aec9ef56aec63c6de4cc5fc8b9190dfb84ddd4f0 Mon Sep 17 00:00:00 2001 From: nayounsang Date: Thu, 5 Jun 2025 16:11:29 +0900 Subject: [PATCH 1/6] feat: add check void option --- .../PreferOptionalChainOptions.ts | 1 + packages/eslint-plugin/src/rules/prefer-optional-chain.ts | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/PreferOptionalChainOptions.ts b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/PreferOptionalChainOptions.ts index de1147b54447..c799268477ee 100644 --- a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/PreferOptionalChainOptions.ts +++ b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/PreferOptionalChainOptions.ts @@ -10,5 +10,6 @@ export interface PreferOptionalChainOptions { checkNumber?: boolean; checkString?: boolean; checkUnknown?: boolean; + checkVoid?: boolean; requireNullish?: boolean; } diff --git a/packages/eslint-plugin/src/rules/prefer-optional-chain.ts b/packages/eslint-plugin/src/rules/prefer-optional-chain.ts index ffbcd213fedd..670c5e9c4713 100644 --- a/packages/eslint-plugin/src/rules/prefer-optional-chain.ts +++ b/packages/eslint-plugin/src/rules/prefer-optional-chain.ts @@ -82,6 +82,11 @@ export default createRule< description: 'Check operands that are typed as `unknown` when inspecting "loose boolean" operands.', }, + checkVoid: { + type: 'boolean', + description: + 'Check operands that are typed as `void` when inspecting "loose boolean" operands.', + }, requireNullish: { type: 'boolean', description: @@ -100,6 +105,7 @@ export default createRule< checkNumber: true, checkString: true, checkUnknown: true, + checkVoid: true, requireNullish: false, }, ], From e35a6f28d02d484d4a962ff7c328f4923bad5c20 Mon Sep 17 00:00:00 2001 From: nayounsang Date: Thu, 5 Jun 2025 16:11:42 +0900 Subject: [PATCH 2/6] test: add test case for check void --- .../prefer-optional-chain.test.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/prefer-optional-chain/prefer-optional-chain.test.ts b/packages/eslint-plugin/tests/rules/prefer-optional-chain/prefer-optional-chain.test.ts index c05f6218b24c..936268ace96d 100644 --- a/packages/eslint-plugin/tests/rules/prefer-optional-chain/prefer-optional-chain.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-optional-chain/prefer-optional-chain.test.ts @@ -1680,6 +1680,22 @@ describe('hand-crafted cases', () => { ], output: 'a?.prop;', }, + // check void + { + code: ` +declare const foo: { + method: undefined | (() => void); +}; +foo.method && foo.method(); + `, + errors: [{ messageId: 'preferOptionalChain' }], + output: ` +declare const foo: { + method: undefined | (() => void); +}; +foo.method?.(); + `, + }, ], valid: [ '!a || !b;', From eacc6225bf1711a9d0a7f21be3edebd8921e3d38 Mon Sep 17 00:00:00 2001 From: nayounsang Date: Thu, 5 Jun 2025 16:25:59 +0900 Subject: [PATCH 3/6] feat: check void type --- .../prefer-optional-chain-utils/gatherLogicalOperands.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/gatherLogicalOperands.ts b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/gatherLogicalOperands.ts index 174f8982cad8..4e8ae6244ea1 100644 --- a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/gatherLogicalOperands.ts +++ b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/gatherLogicalOperands.ts @@ -111,6 +111,11 @@ function isValidFalseBooleanCheckType( if (options.checkBigInt === true) { allowedFlags |= ts.TypeFlags.BigIntLike; } + + if (options.checkVoid === true) { + allowedFlags |= ts.TypeFlags.Void; + } + return types.every(t => isTypeFlagSet(t, allowedFlags)); } From 88decc1ebd0e51be701f774e2b0425c5164a7b04 Mon Sep 17 00:00:00 2001 From: nayounsang Date: Thu, 5 Jun 2025 16:32:35 +0900 Subject: [PATCH 4/6] chore: add snapshot --- .../tests/schema-snapshots/prefer-optional-chain.shot | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/eslint-plugin/tests/schema-snapshots/prefer-optional-chain.shot b/packages/eslint-plugin/tests/schema-snapshots/prefer-optional-chain.shot index 7d300bdbdfdd..8ec3cc46b7b5 100644 --- a/packages/eslint-plugin/tests/schema-snapshots/prefer-optional-chain.shot +++ b/packages/eslint-plugin/tests/schema-snapshots/prefer-optional-chain.shot @@ -33,6 +33,10 @@ "description": "Check operands that are typed as `unknown` when inspecting \"loose boolean\" operands.", "type": "boolean" }, + "checkVoid": { + "description": "Check operands that are typed as `void` when inspecting \"loose boolean\" operands.", + "type": "boolean" + }, "requireNullish": { "description": "Skip operands that are not typed with `null` and/or `undefined` when inspecting \"loose boolean\" operands.", "type": "boolean" @@ -61,6 +65,8 @@ type Options = [ checkString?: boolean; /** Check operands that are typed as `unknown` when inspecting "loose boolean" operands. */ checkUnknown?: boolean; + /** Check operands that are typed as `void` when inspecting "loose boolean" operands. */ + checkVoid?: boolean; /** Skip operands that are not typed with `null` and/or `undefined` when inspecting "loose boolean" operands. */ requireNullish?: boolean; }, From 4171c5e4f536824127cd5b49a6818b8ed6f43b79 Mon Sep 17 00:00:00 2001 From: nayounsang Date: Thu, 5 Jun 2025 16:51:48 +0900 Subject: [PATCH 5/6] docs: add checkvoid section --- .../docs/rules/prefer-optional-chain.mdx | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/eslint-plugin/docs/rules/prefer-optional-chain.mdx b/packages/eslint-plugin/docs/rules/prefer-optional-chain.mdx index 777c80068e69..6fa0720ba2c3 100644 --- a/packages/eslint-plugin/docs/rules/prefer-optional-chain.mdx +++ b/packages/eslint-plugin/docs/rules/prefer-optional-chain.mdx @@ -265,6 +265,37 @@ thing?.toString(); +### `checkVoid` + +{/* insert option description */} + +Examples of code for this rule with `{ checkVoid: true }`: + + + + +```ts option='{ "checkVoid": true }' +declare const thing: { + method: undefined | (() => void); +}; + +thing.method && thing.method(); +``` + + + + +```ts option='{ "checkVoid": true }' +declare const thing: { + method: undefined | (() => void); +}; + +thing.method?.(); +``` + + + + ### `requireNullish` {/* insert option description */} From 791461b8f14991f41acfeae54b9167323ab83c20 Mon Sep 17 00:00:00 2001 From: nayounsang Date: Thu, 5 Jun 2025 17:11:22 +0900 Subject: [PATCH 6/6] chore: update snapshot --- .../prefer-optional-chain.shot | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-optional-chain.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-optional-chain.shot index 8607ddca5bca..032898689ebb 100644 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-optional-chain.shot +++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-optional-chain.shot @@ -149,6 +149,25 @@ declare const thing: bigint; thing?.toString(); +Incorrect +Options: { "checkVoid": true } + +declare const thing: { + method: undefined | (() => void); +}; + +thing.method && thing.method(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Prefer using an optional chain expression instead, as it's more concise and easier to read. + +Correct +Options: { "checkVoid": true } + +declare const thing: { + method: undefined | (() => void); +}; + +thing.method?.(); + Incorrect Options: { "requireNullish": true }