Skip to content

feat(eslint-plugin): [await-thenable] check Promise.all #10282

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
wants to merge 5 commits into from

Conversation

Zamiell
Copy link
Contributor

@Zamiell Zamiell commented Nov 4, 2024

PR Checklist

Overview

  • I added logic for a new type of check for Promise.all, Promise.allSettled, and Promise.race. As discussed in the aforementioned issue, we are arbitrarily adding this to await-thenable instead of putting it in into a new rule.
  • I did not put the new logic behind an option because of YAGNI principle. If that is required, I can add an option.
  • The type verification part probably needs to be reviewed, since I am not sure of the best way to check that an arbitrary ts.Type matches the form of Iterable<Promise<any>>. So for now, I just check for the most common case with hard-coded names.

@typescript-eslint
Copy link
Contributor

Thanks for the PR, @Zamiell!

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.

Copy link

netlify bot commented Nov 4, 2024

Deploy Preview for typescript-eslint ready!

Name Link
🔨 Latest commit 55513d4
🔍 Latest deploy log https://app.netlify.com/sites/typescript-eslint/deploys/672b7a31046b560008e63704
😎 Deploy Preview https://deploy-preview-10282--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: 97 (🟢 up 5 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.

Copy link

nx-cloud bot commented Nov 4, 2024

☁️ Nx Cloud Report

CI is running/has finished running commands for commit 55513d4. As they complete they will appear below. Click to see the status, the terminal output, and the build insights.

📂 See all runs for this CI Pipeline Execution


✅ Successfully ran 2 targets

Sent with 💌 from NxCloud.

@Zamiell Zamiell changed the title feat(eslint-plugin): [await thenable] check Promise.all [await thenable] check Promise.all Nov 4, 2024
@Zamiell Zamiell changed the title [await thenable] check Promise.all feat(eslint-plugin): [await-thenable] check Promise.all Nov 4, 2024
Copy link

codecov bot commented Nov 4, 2024

Codecov Report

Attention: Patch coverage is 66.66667% with 6 lines in your changes missing coverage. Please review.

Project coverage is 86.54%. Comparing base (c13b6b4) to head (55513d4).
Report is 282 commits behind head on main.

Files with missing lines Patch % Lines
packages/eslint-plugin/src/rules/await-thenable.ts 66.66% 5 Missing and 1 partial ⚠️

❌ Your patch status has failed because the patch coverage (66.66%) is below the target coverage (90.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #10282      +/-   ##
==========================================
- Coverage   86.56%   86.54%   -0.03%     
==========================================
  Files         431      431              
  Lines       15188    15206      +18     
  Branches     4418     4425       +7     
==========================================
+ Hits        13148    13160      +12     
- Misses       1683     1688       +5     
- Partials      357      358       +1     
Flag Coverage Δ
unittest 86.54% <66.66%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
packages/eslint-plugin/src/rules/await-thenable.ts 89.28% <66.66%> (-10.72%) ⬇️

if (
node.callee.type === AST_NODE_TYPES.MemberExpression &&
node.callee.object.type === AST_NODE_TYPES.Identifier &&
node.callee.object.name === 'Promise' &&
Copy link
Member

Choose a reason for hiding this comment

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

The type verification part probably needs to be reviewed, ...

[Bug] Good question. isBuiltinSymbolLike is our standard utility for this. You'll want to use that, and add tests to make sure things coincidentally named Array or Promise don't erroneously trigger this rule.

Copy link
Member

@kirkwaiblinger kirkwaiblinger Nov 11, 2024

Choose a reason for hiding this comment

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

};
},
});

/** This is a simplified version of the `getTypeName` utility function. */
Copy link
Member

Choose a reason for hiding this comment

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

[Question] Why use this the existing getTypeName, then?

This might be a moot point if you end up using isBuiltinSymbolLike or one of the functions around it.

Comment on lines +188 to +189
const argument = node.arguments[0];
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
Copy link
Member

Choose a reason for hiding this comment

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

[Style] 💡 hint:

Suggested change
const argument = node.arguments[0];
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
const argument = node.arguments.at(0);

declare const booleans: boolean[];
await Promise.all(booleans);
await Promise.allSettled(booleans);
await Promise.race(booleans);
Copy link
Member

Choose a reason for hiding this comment

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

@JoshuaKGoldberg JoshuaKGoldberg added the awaiting response Issues waiting for a reply from the OP or another party label Nov 10, 2024
@kirkwaiblinger
Copy link
Member

If we go with this PR, we should add co-author attributions to @abrahamguo and @Tjstretchalot for #10163 and #8094.

@kirkwaiblinger
Copy link
Member

  • The type verification part probably needs to be reviewed, since I am not sure of the best way to check that an arbitrary ts.Type matches the form of Iterable<Promise<any>>. So for now, I just check for the most common case with hard-coded names.

Sadly, TS's getIterationTypesOfIterable is basically exactly what we'd need for this, but it's not exposed. Feel free to open an issue with TS if you have spare time! But, yeah, we'll need to find a solution in the meantime...

I was also wondering if we can do something roughly like

function getIteratedType(checker: ts.TypeChecker, type: ts.Type): ts.Type | undefined {
  const symbolIterator = tsutils.getWellKnownSymbolPropertyOfType(type, 'iterator', checker);
  if (symbolIterator == null) {
    return;
  }

  const symbolIteratorType = checker.getTypeOfSymbol(symbolIterator);
  const symbolIteratorCallSignatures = tsutils.getCallSignaturesOfType(symbolIteratorType);
  const returnTypes = symbolIteratorCallSignatures.map(signature => signature.getReturnType());
  // figure out how to make a union of these types or just return the list :shrug:
  return returnTypes
}

As for checking whether something looks like a promise, we generally use the tsutils.isThenableType() function.

const typeName = getTypeNameSimple(type);
const typeArgName = getTypeNameSimple(typeArg);

if (typeName === 'Array' && typeArgName !== 'Promise') {
Copy link
Member

Choose a reason for hiding this comment

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

@JoshuaKGoldberg
Copy link
Member

👋 Hey @Zamiell! Just checking in, is this still something you have time for? No worries if not - I just don't want to leave it hanging.

@Zamiell
Copy link
Contributor Author

Zamiell commented Dec 8, 2024

Yeah I'll try to get to it this weekend if I have time.

@JoshuaKGoldberg
Copy link
Member

👋 Checking in again @Zamiell, happy new year! Is this still on your radar?

@Zamiell
Copy link
Contributor Author

Zamiell commented Jan 10, 2025

yeah i still plan to work on this when i get some more free time, i have been very sick lately

@JoshuaKGoldberg
Copy link
Member

Same as #10265 (comment): ❤️ hope you feel better soon!

Removing stale and setting as a draft for now. If this is still pending in February I'd say we should close it out to un-block the issue.

@JoshuaKGoldberg JoshuaKGoldberg marked this pull request as draft January 10, 2025 13:49
@JoshuaKGoldberg
Copy link
Member

Closing out to un-block the issue, as planned. Cheers!

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 10, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
awaiting response Issues waiting for a reply from the OP or another party
Projects
None yet
3 participants