Skip to content

feat(eslint-plugin): [no-confusing-void-expression] add ignoreVoidInVoid option to void in void situation #8632

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3492cb6
feat: ignoreVoidInVoid option add
developer-bandi Mar 9, 2024
7bc38a4
test: ignoreVoidInVoid test case add
developer-bandi Mar 9, 2024
8c4b72f
docs: ignoreVoidInVoid description add
developer-bandi Mar 9, 2024
028aa93
fix: snapshot update
developer-bandi Mar 9, 2024
7babaf9
test: add test code
developer-bandi Mar 9, 2024
0030a7b
docs: edit example
developer-bandi Mar 10, 2024
3ed4122
fix: functionExprssion and inside block return fix
developer-bandi Mar 10, 2024
b76656a
test: change void expression to console.log
developer-bandi Mar 10, 2024
27d8d5f
fix: apply code review
developer-bandi Mar 10, 2024
39c2080
fix: add code review
developer-bandi Mar 11, 2024
0f88a30
test: add additional test code
developer-bandi Mar 11, 2024
0d547c9
test add test case
developer-bandi Mar 11, 2024
a7e7de7
refactor: if statement to single return
developer-bandi Mar 11, 2024
d72be7b
fix: variable declator expection
developer-bandi Mar 12, 2024
48eb394
fix: code reivew apply
developer-bandi Mar 12, 2024
8a7a8e7
docs: remove comment
developer-bandi Mar 13, 2024
f9c30dc
feat: allow arrow function type
developer-bandi Mar 24, 2024
024993f
fix: reflect code review
developer-bandi Mar 25, 2024
f392b39
fix: any,unknown type disallow and refactor
developer-bandi Mar 30, 2024
ea12937
refactor: make hasVoidreturnType
developer-bandi Mar 31, 2024
b04a9be
refactor: change function name
developer-bandi Apr 7, 2024
9ed6061
Merge branch 'main' of https://github.com/developer-bandi/typescript-…
developer-bandi Apr 10, 2024
896a85c
test: snapshot update
developer-bandi Apr 10, 2024
6cc46ad
fix: lint fix with error line fix
developer-bandi Apr 10, 2024
09846c0
Merge branch 'main' of https://github.com/developer-bandi/typescript-…
developer-bandi Apr 28, 2024
7957082
test: add test code
developer-bandi Apr 28, 2024
369f4e7
test: fix test case
developer-bandi Apr 28, 2024
5428e4b
fix: not apply not any, unknown alias type
developer-bandi May 3, 2024
a522d26
fix: codereview reflect
developer-bandi May 5, 2024
edbfc36
fix: test code
developer-bandi Jun 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/eslint-plugin/docs/rules/no-confusing-void-expression.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,24 @@ function doSomething() {
console.log(void alert('Hello, world!'));
```

### `ignoreVoidInVoid`

Allow using `void` type expressions in return value of a function that specified as `void`

Examples of additional **correct** code with this option enabled:

```ts option='{ "ignoreVoidInVoid": true }' showPlaygroundButton
function test1(value: string): void {
return window.postMessage(value);
}

const test2 = (value: string): void => window.postMessage(value);

const test3 = (value: string): void => {
return window.postMessage(value);
};
```

## When Not To Use It

The return type of a function can be inspected by going to its definition or hovering over it in an IDE.
Expand Down
89 changes: 87 additions & 2 deletions packages/eslint-plugin/src/rules/no-confusing-void-expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ import {
nullThrows,
NullThrowsReasons,
} from '../util';
import {
ancestorHasReturnType,
isValidFunctionExpressionReturnType,
} from '../util/explicitReturnTypeUtils';

export type Options = [
{
ignoreArrowShorthand?: boolean;
ignoreVoidOperator?: boolean;
ignoreVoidInVoid?: boolean;
},
];

Expand All @@ -32,6 +37,26 @@ export type MessageId =
| 'invalidVoidExprWrapVoid'
| 'voidExprWrapVoid';

function findFunction(
node: TSESTree.Node | undefined,
):
| TSESTree.FunctionExpression
| TSESTree.ArrowFunctionExpression
| TSESTree.FunctionDeclaration
| null {
if (!node) {
return null;
}
if (
node.type === AST_NODE_TYPES.FunctionExpression ||
node.type === AST_NODE_TYPES.ArrowFunctionExpression ||
node.type === AST_NODE_TYPES.FunctionDeclaration
) {
return node;
}
return findFunction(node.parent);
}

export default createRule<Options, MessageId>({
name: 'no-confusing-void-expression',
meta: {
Expand Down Expand Up @@ -72,6 +97,7 @@ export default createRule<Options, MessageId>({
properties: {
ignoreArrowShorthand: { type: 'boolean' },
ignoreVoidOperator: { type: 'boolean' },
ignoreVoidInVoid: { type: 'boolean' },
},
additionalProperties: false,
},
Expand All @@ -80,8 +106,13 @@ export default createRule<Options, MessageId>({
fixable: 'code',
hasSuggestions: true,
},
defaultOptions: [{ ignoreArrowShorthand: false, ignoreVoidOperator: false }],

defaultOptions: [
{
ignoreArrowShorthand: false,
ignoreVoidOperator: false,
ignoreVoidInVoid: false,
},
],
create(context, [options]) {
return {
'AwaitExpression, CallExpression, TaggedTemplateExpression'(
Expand All @@ -98,6 +129,7 @@ export default createRule<Options, MessageId>({
}

const invalidAncestor = findInvalidAncestor(node);

if (invalidAncestor == null) {
// void expression is in valid position
return;
Expand All @@ -112,6 +144,12 @@ export default createRule<Options, MessageId>({
if (invalidAncestor.type === AST_NODE_TYPES.ArrowFunctionExpression) {
// handle arrow function shorthand

if (options.ignoreVoidInVoid) {
if (hasValidReturnType(invalidAncestor)) {
return;
}
}

if (options.ignoreVoidOperator) {
// handle wrapping with `void`
return context.report({
Expand Down Expand Up @@ -167,6 +205,14 @@ export default createRule<Options, MessageId>({
if (invalidAncestor.type === AST_NODE_TYPES.ReturnStatement) {
// handle return statement

if (options.ignoreVoidInVoid) {
const functionNode = findFunction(invalidAncestor);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Bug] The following code won't be reported, because findFunction doesn't stop on function declarations:

type Foo = () => void;

const test1: Foo = function () {
  function testtt() {
    return console.log(); // not reported - bug
  }
};


const test2 = function () {
  function testtt() {
    return console.log(); // reported - nice
  }
};

playground


if (hasValidReturnType(functionNode)) {
return;
}
}

if (options.ignoreVoidOperator) {
// handle wrapping with `void`
return context.report({
Expand Down Expand Up @@ -376,5 +422,44 @@ export default createRule<Options, MessageId>({
const type = getConstrainedTypeAtLocation(services, targetNode);
return tsutils.isTypeFlagSet(type, ts.TypeFlags.VoidLike);
}

function hasValidReturnType(
node:
| TSESTree.FunctionExpression
| TSESTree.ArrowFunctionExpression
| TSESTree.FunctionDeclaration
| null,
): boolean {
const services = getParserServices(context);

if (node != null) {
const functionTSNode = services.esTreeNodeToTSNodeMap.get(node);
const functionType = services.getTypeAtLocation(node);

if (functionTSNode.type) {
const signatures = tsutils.getCallSignaturesOfType(functionType);

return !signatures.every(signature =>
tsutils.isTypeFlagSet(
signature.getReturnType(),
ts.TypeFlags.Any | ts.TypeFlags.Unknown,
),
);
}

if (
((node.type === AST_NODE_TYPES.FunctionExpression ||
node.type === AST_NODE_TYPES.ArrowFunctionExpression) &&
isValidFunctionExpressionReturnType(node, {
allowTypedFunctionExpressions: true,
})) ||
ancestorHasReturnType(node)
) {
return true;
}
Comment on lines +450 to +459
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we check that function signature don't contain any and unknown in the return type, we should do the same for typed functions/variable declarations. The following cases are not reported:

type HigherOrderType = () => any;
(): HigherOrderType => () => console.log();

const x: HigherOrderType = () => console.log();

playground

}

return false;
}
},
});

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading