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 */} 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-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)); } 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, }, ], 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 } 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;', 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; },