Skip to content
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

fix(eslint-plugin): [explicit-function-return-type] support JSX attributes in allowTypedFunctionExpressions #7553

Merged

Conversation

merrywhether
Copy link
Contributor

PR Checklist

Overview

Adds support for JSX attributes to the allowTypedFunctionExpressions option of the explicit-function-return-type rule by augmenting the isTypedFunctionExpression() function to include more parent types. Also DRYes out code between common and recursive object-property cases.

@typescript-eslint
Copy link
Contributor

Thanks for the PR, @merrywhether!

typescript-eslint is a 100% community driven project, and we are incredibly grateful that you are contributing to that community.

The core maintainers work on this in their personal time, so please understand that it may not be possible for them to review your work immediately.

Thanks again!


🙏 Please, if you or your company is finding typescript-eslint valuable, help us sustain the project by sponsoring it transparently on https://opencollective.com/typescript-eslint.

@netlify
Copy link

netlify bot commented Aug 28, 2023

Deploy Preview for typescript-eslint ready!

Name Link
🔨 Latest commit a4df798
🔍 Latest deploy log https://app.netlify.com/sites/typescript-eslint/deploys/65492482e2be480008be5c72
😎 Deploy Preview https://deploy-preview-7553--typescript-eslint.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 99 (no change from production)
Accessibility: 100 (no change from production)
Best Practices: 92 (no change from production)
SEO: 98 (no change from production)
PWA: 80 (no change from production)
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify site configuration.

* <Comp x={...} />
* ```
*/
function isJSXAttribute(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Open to feedback on the name here.

node: TSESTree.Node,
): node is TSESTree.JSXExpressionContainer | TSESTree.JSXSpreadAttribute {
return (
node.type === AST_NODE_TYPES.JSXExpressionContainer ||
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is technically the relevant RHS type of JSXAttribute, so this check could also recurse one level higher if that is preferred. This should be the only way to directly supply a function in JSX though.

Copy link
Member

Choose a reason for hiding this comment

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

I think we'll want to check the parent because this is another valid location for a JSXExpressionContainer:

<>{() => {}}</>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bradzacher Ah, I totally forgot about the render-props pattern, it's been a while since their heyday! Running this down actually led me across an existing issue in TS where fragment shorthands are not typed properly: microsoft/TypeScript#50429

I went around on this for a while, but I again think all JSXExpressionContainer instances are fully bound by types barring that issue (reference CodeSandbox with render props):

  1. Attribute assignment: original case; type is constrained by assignment
  2. Render props of built-ins and <Fragment />: not allowed due to limited child types (JSX.ElementType or JSX.Element | null); all functions will be type errors here
  3. Render props of <> fragment shorthand: all functions should be type errors here
  4. Render props of components with custom function children: properly typed-constrained by explicit children prop, the same as if children was defined via attribute

I eventually realized that while something like this is unsafe:

<>{() => 'bug'}</>

Having a lint error that turns it into this doesn't make things better as it's still not a type error but won't work as expected at runtime (especially if you return an object instead):

<>{(): string => 'bug'}</>

While this is a currently an issue with TypeScript, it doesn't seem like the lint rule can do anything better here in place of proper type-checking. But functions can be valid in children expression containers generally.

Reference TS-ESLint playground

isPropertyOfObjectWithType(parent) ||
isFunctionArgument(parent, node) ||
isConstructorArgument(parent)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

DRYing these out, it makes it more obvious that the recursive object-property case doesn't include this isConstructorArgument() case, but it seems like it should?

Happy to move it into isTypedParent() and add tests if this is indeed a bug.

@bradzacher bradzacher added the bug Something isn't working label Sep 8, 2023
@bradzacher bradzacher changed the title fix(eslint-plugin): support JSX attrs in allowTypedFunctionExpressions fix(eslint-plugin): [explicit-function-return-type] support JSX attributes in allowTypedFunctionExpressions Sep 8, 2023
Copy link
Member

@bradzacher bradzacher left a comment

Choose a reason for hiding this comment

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

this is looking good so far!
good work adding all the test cases!

node: TSESTree.Node,
): node is TSESTree.JSXExpressionContainer | TSESTree.JSXSpreadAttribute {
return (
node.type === AST_NODE_TYPES.JSXExpressionContainer ||
Copy link
Member

Choose a reason for hiding this comment

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

I think we'll want to check the parent because this is another valid location for a JSXExpressionContainer:

<>{() => {}}</>

{
code: `
const Comp: FC = () => {
return <button {...{ onClick: () => {} }} />;
};
`,
options: [{ allowTypedFunctionExpressions: true }],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
Copy link
Member

Choose a reason for hiding this comment

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

for completeness can we also get this as an invalid test?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added negated counterparts of all new tests

@bradzacher bradzacher added the awaiting response Issues waiting for a reply from the OP or another party label Sep 8, 2023
@merrywhether merrywhether force-pushed the explicit-return-type-jsx branch 3 times, most recently from 9b08f41 to 9abccf6 Compare September 12, 2023 06:32
@merrywhether
Copy link
Contributor Author

@bradzacher Updated this, LMK what you think

@github-actions github-actions bot removed the awaiting response Issues waiting for a reply from the OP or another party label Sep 25, 2023
@merrywhether
Copy link
Contributor Author

@bradzacher Anything I can do to help land this?

Copy link
Member

@bradzacher bradzacher left a comment

Choose a reason for hiding this comment

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

this looks good to me - thanks for this!

@bradzacher bradzacher merged commit be2777c into typescript-eslint:main Nov 10, 2023
44 of 46 checks passed
@merrywhether merrywhether deleted the explicit-return-type-jsx branch November 10, 2023 16:07
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 18, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[explicit-function-return-type] allowTypedFunctionExpressions option should be aware of JSX
2 participants