Skip to content

Commit

Permalink
fix(eslint-plugin): [explicit-function-return-type] support JSX attri…
Browse files Browse the repository at this point in the history
…butes in `allowTypedFunctionExpressions` (#7553)
  • Loading branch information
merrywhether committed Nov 10, 2023
1 parent eb736bb commit be2777c
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 28 deletions.
Expand Up @@ -172,6 +172,10 @@ functionWithObjectArg({
return 1;
},
});

const Comp: FC = () => {
return <button onClick={() => {}} />;
};
```

### `allowHigherOrderFunctions`
Expand Down
77 changes: 49 additions & 28 deletions packages/eslint-plugin/src/util/explicitReturnTypeUtils.ts
Expand Up @@ -37,6 +37,53 @@ function isPropertyDefinitionWithTypeAnnotation(
);
}

/**
* Checks if a node belongs to:
* ```
* foo(() => 1)
* ```
*/
function isFunctionArgument(
parent: TSESTree.Node,
callee?: FunctionExpression,
): parent is TSESTree.CallExpression {
return (
parent.type === AST_NODE_TYPES.CallExpression &&
// make sure this isn't an IIFE
parent.callee !== callee
);
}

/**
* Checks if a node is type-constrained in JSX
* ```
* <Foo x={() => {}} />
* <Bar>{() => {}}</Bar>
* <Baz {...props} />
* ```
*/
function isTypedJSX(
node: TSESTree.Node,
): node is TSESTree.JSXExpressionContainer | TSESTree.JSXSpreadAttribute {
return (
node.type === AST_NODE_TYPES.JSXExpressionContainer ||
node.type === AST_NODE_TYPES.JSXSpreadAttribute
);
}

function isTypedParent(
parent: TSESTree.Node,
callee?: FunctionExpression,
): boolean {
return (
isTypeAssertion(parent) ||
isVariableDeclaratorWithTypeAnnotation(parent) ||
isPropertyDefinitionWithTypeAnnotation(parent) ||
isFunctionArgument(parent, callee) ||
isTypedJSX(parent)
);
}

/**
* Checks if a node belongs to:
* ```
Expand Down Expand Up @@ -78,13 +125,7 @@ function isPropertyOfObjectWithType(
return false;
}

return (
isTypeAssertion(parent) ||
isPropertyDefinitionWithTypeAnnotation(parent) ||
isVariableDeclaratorWithTypeAnnotation(parent) ||
isFunctionArgument(parent) ||
isPropertyOfObjectWithType(parent)
);
return isTypedParent(parent) || isPropertyOfObjectWithType(parent);
}

/**
Expand Down Expand Up @@ -127,23 +168,6 @@ function doesImmediatelyReturnFunctionExpression({
);
}

/**
* Checks if a node belongs to:
* ```
* foo(() => 1)
* ```
*/
function isFunctionArgument(
parent: TSESTree.Node,
callee?: FunctionExpression,
): parent is TSESTree.CallExpression {
return (
parent.type === AST_NODE_TYPES.CallExpression &&
// make sure this isn't an IIFE
parent.callee !== callee
);
}

/**
* Checks if a function belongs to:
* ```
Expand Down Expand Up @@ -194,11 +218,8 @@ function isTypedFunctionExpression(
}

return (
isTypeAssertion(parent) ||
isVariableDeclaratorWithTypeAnnotation(parent) ||
isPropertyDefinitionWithTypeAnnotation(parent) ||
isTypedParent(parent, node) ||
isPropertyOfObjectWithType(parent) ||
isFunctionArgument(parent, node) ||
isConstructorArgument(parent)
);
}
Expand Down
Expand Up @@ -180,6 +180,53 @@ class App {
`,
options: [{ allowTypedFunctionExpressions: true }],
},
// https://github.com/typescript-eslint/typescript-eslint/issues/7552
{
code: 'const foo = <button onClick={() => {}} />;',
options: [{ allowTypedFunctionExpressions: true }],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
{
code: 'const foo = <button on={{ click: () => {} }} />;',
options: [{ allowTypedFunctionExpressions: true }],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
{
code: 'const foo = <Bar>{() => {}}</Bar>;',
options: [{ allowTypedFunctionExpressions: true }],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
{
code: 'const foo = <Bar>{{ on: () => {} }}</Bar>;',
options: [{ allowTypedFunctionExpressions: true }],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
{
code: 'const foo = <button {...{ onClick: () => {} }} />;',
options: [{ allowTypedFunctionExpressions: true }],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},

// https://github.com/typescript-eslint/typescript-eslint/issues/525
{
code: `
Expand Down Expand Up @@ -977,6 +1024,96 @@ const x: Foo = {
},
],
},
{
code: 'const foo = <button onClick={() => {}} />;',
options: [{ allowTypedFunctionExpressions: false }],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
errors: [
{
messageId: 'missingReturnType',
line: 1,
endLine: 1,
column: 33,
endColumn: 35,
},
],
},
{
code: 'const foo = <button on={{ click: () => {} }} />;',
options: [{ allowTypedFunctionExpressions: false }],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
errors: [
{
messageId: 'missingReturnType',
line: 1,
endLine: 1,
column: 27,
endColumn: 34,
},
],
},
{
code: 'const foo = <Bar>{() => {}}</Bar>;',
options: [{ allowTypedFunctionExpressions: false }],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
errors: [
{
messageId: 'missingReturnType',
line: 1,
endLine: 1,
column: 22,
endColumn: 24,
},
],
},
{
code: 'const foo = <Bar>{{ on: () => {} }}</Bar>;',
options: [{ allowTypedFunctionExpressions: false }],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
errors: [
{
messageId: 'missingReturnType',
line: 1,
endLine: 1,
column: 21,
endColumn: 25,
},
],
},
{
code: 'const foo = <button {...{ onClick: () => {} }} />;',
options: [{ allowTypedFunctionExpressions: false }],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
errors: [
{
messageId: 'missingReturnType',
line: 1,
endLine: 1,
column: 27,
endColumn: 36,
},
],
},
{
code: '() => () => {};',
options: [{ allowHigherOrderFunctions: true }],
Expand Down

0 comments on commit be2777c

Please sign in to comment.