From aec9ef56aec63c6de4cc5fc8b9190dfb84ddd4f0 Mon Sep 17 00:00:00 2001 From: nayounsang Date: Thu, 5 Jun 2025 16:11:29 +0900 Subject: [PATCH 1/9] 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/9] 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/9] 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/9] 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/9] 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/9] 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 } From b3d2a4fc9dc7b8f310d32f234afa67bbb924543b Mon Sep 17 00:00:00 2001 From: nayounsang Date: Sat, 7 Jun 2025 17:22:41 +0900 Subject: [PATCH 7/9] refactor: inject option based on flagsToExcludeFromCheck --- .../gatherLogicalOperands.ts | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) 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 4e8ae6244ea1..ab6f90b121a9 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 @@ -59,8 +59,6 @@ export interface InvalidOperand { type: OperandValidity.Invalid; } type Operand = InvalidOperand | ValidOperand; - -const NULLISH_FLAGS = ts.TypeFlags.Null | ts.TypeFlags.Undefined; function isValidFalseBooleanCheckType( node: TSESTree.Node, disallowFalseyLiteral: boolean, @@ -92,31 +90,30 @@ function isValidFalseBooleanCheckType( return false; } - let allowedFlags = NULLISH_FLAGS | ts.TypeFlags.Object; - if (options.checkAny === true) { - allowedFlags |= ts.TypeFlags.Any; + let flagsToExcludeFromCheck = 0; + if (options.checkAny !== true) { + flagsToExcludeFromCheck |= ts.TypeFlags.Any; } - if (options.checkUnknown === true) { - allowedFlags |= ts.TypeFlags.Unknown; + if (options.checkUnknown !== true) { + flagsToExcludeFromCheck |= ts.TypeFlags.Unknown; } - if (options.checkString === true) { - allowedFlags |= ts.TypeFlags.StringLike; + if (options.checkString !== true) { + flagsToExcludeFromCheck |= ts.TypeFlags.StringLike; } - if (options.checkNumber === true) { - allowedFlags |= ts.TypeFlags.NumberLike; + if (options.checkNumber !== true) { + flagsToExcludeFromCheck |= ts.TypeFlags.NumberLike; } - if (options.checkBoolean === true) { - allowedFlags |= ts.TypeFlags.BooleanLike; + if (options.checkBoolean !== true) { + flagsToExcludeFromCheck |= ts.TypeFlags.BooleanLike; } - if (options.checkBigInt === true) { - allowedFlags |= ts.TypeFlags.BigIntLike; + if (options.checkBigInt !== true) { + flagsToExcludeFromCheck |= ts.TypeFlags.BigIntLike; } - - if (options.checkVoid === true) { - allowedFlags |= ts.TypeFlags.Void; + if (options.checkVoid !== true) { + flagsToExcludeFromCheck |= ts.TypeFlags.Void; } - return types.every(t => isTypeFlagSet(t, allowedFlags)); + return types.every(t => !isTypeFlagSet(t, flagsToExcludeFromCheck)); } export function gatherLogicalOperands( From 656ec507805e8d67a23653389de4c93986ca7f48 Mon Sep 17 00:00:00 2001 From: nayounsang Date: Sat, 7 Jun 2025 17:23:05 +0900 Subject: [PATCH 8/9] test: add test for checkVoid = false --- .../prefer-optional-chain/prefer-optional-chain.test.ts | 9 +++++++++ 1 file changed, 9 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 936268ace96d..dd68bcfd6ac1 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 @@ -1894,6 +1894,15 @@ foo.method?.(); `, options: [{ checkUnknown: false }], }, + { + code: ` +declare const foo: { + method: undefined | (() => void); +}; +foo.method && foo.method(); + `, + options: [{ checkVoid: false }], + }, '(x = {}) && (x.y = true) != null && x.y.toString();', "('x' as `${'x'}`) && ('x' as `${'x'}`).length;", '`x` && `x`.length;', From 4b92ad4340a6128d29f50f9b92fc68baf645a794 Mon Sep 17 00:00:00 2001 From: nayounsang Date: Thu, 12 Jun 2025 17:20:44 +0900 Subject: [PATCH 9/9] test: add tc for all checks are excluded but invaild --- .../prefer-optional-chain.test.ts | 26 +++++++++++++++++++ 1 file changed, 26 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 dd68bcfd6ac1..caf2a232b554 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 @@ -1696,6 +1696,32 @@ declare const foo: { foo.method?.(); `, }, + // Exclude for everything else, an error occurs + { + code: noFormat`declare const foo: { x: { y: string } } | null; foo && foo.x;`, + errors: [ + { + messageId: 'preferOptionalChain', + suggestions: [ + { + messageId: 'optionalChainSuggest', + output: `declare const foo: { x: { y: string } } | null; foo?.x;`, + }, + ], + }, + ], + options: [ + { + checkAny: false, + checkBigInt: false, + checkBoolean: false, + checkNumber: false, + checkString: false, + checkUnknown: false, + checkVoid: false, + }, + ], + }, ], valid: [ '!a || !b;', pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy