Skip to content

Bug: promise-function-async is reporting on () => A | Promise<B> #10687

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
4 tasks done
fregante opened this issue Jan 20, 2025 · 6 comments · Fixed by #10950
Closed
4 tasks done

Bug: promise-function-async is reporting on () => A | Promise<B> #10687

fregante opened this issue Jan 20, 2025 · 6 comments · Fixed by #10950
Labels
accepting prs Go ahead, send a pull request that resolves this issue bug Something isn't working locked due to age Please open a new issue if you'd like to say more. See https://typescript-eslint.io/contributing. package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin

Comments

@fregante
Copy link
Contributor

Before You File a Bug Report Please Confirm You Have Done The Following...

  • I have tried restarting my IDE and the issue persists.
  • I have updated to the latest version of the packages.
  • I have searched for related issues and found none that matched my issue.
  • I have read the FAQ and my problem is not listed.

Playground Link

https://typescript-eslint.io/play/#ts=5.7.2&fileType=.ts&code=FAMwrgdgxgLglgewgAgEYAoCUyDexnIBOApjGISgLICGMAFgHSHUQAmCAtlsgPzIDkSYv2QAuZAAVCnOAGdiTYrIQAbAG7F0ARkwBuYAF8gA&eslintrc=N4KABGBEBOCuA2BTAzpAXGUEKQAIBcBPABxQGNoBLY-AWhXkoDt8B6Y6AewFtLlFaAM1hMy%2BSpya0AhskKj0URNC7RI4MAF8QmoA&tsconfig=N4KABGBEDGD2C2AHAlgGwKYCcDyiAuysAdgM6QBcYoEEkJemy0eAcgK6qoDCAFutAGsylBm3TgwAXxCSgA&tokens=false

Repro Code

function b() {
  return Math.random() ? 'one' : Promise.resolve(1);
}

ESLint Config

_playground default_

tsconfig

_playground default_

Expected Result

No reports

Actual Result

"Functions that return promises must be async."

Attempts to fix to async function, changing the return type from A | Promise<B> to Promise<A | B>

Additional Info

While this behavior is described in the docs/demo, I believe it's wrong because the function does not "return a promise," it only "MAY return a promise."

The change requested is unsafe as it changes the signature/type of the function entirely.

Note that whether () => A | Promise<B> is a good type is not relevant to this issue. The type exists in the wild (web extensions, screenshot) and it can be prohibited by a separate rule (suggested here)

@fregante fregante added bug Something isn't working package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin triage Waiting for team members to take a look labels Jan 20, 2025
@bradzacher
Copy link
Member

bradzacher commented Jan 20, 2025

As you mentioned - the rule docs explicitly state this and they also include a workaround if it is your intention to return a non-promise and a promise:

If it is intentional, make the return type explicitly to allow the rule to pass.


The change requested is unsafe as it changes the signature/type of the function entirely.

The change requested is a suggestion fixer which must be manually applied by a user. Suggestion fixers (unlike autofixers) are allowed to be unsafe or incomplete as they must be manually actioned.

@fregante
Copy link
Contributor Author

fregante commented Jan 20, 2025

If it is intentional, make the return type explicitly to allow the rule to pass.

But this is not what the error says: "Functions that return promises must be async."

The error message, as is, is incomplete/incorrect because the function does not return only a promise.

The reason why I opened this issue is specifically because I found an eslint-disable promise-function-async explanation that missed this subtlety.


Changes suggested, one or more:

  1. improve error message for this case
    • e.g. "Functions that return promises must be async or have an explicit return type"
  2. remove this exception, report regardless of return type presence
    • because I don’t think inference should validate or invalidate this rule
  3. add a fixer to append the return type instead of adding async
  4. extract A | Promise<B> reporting to its own rule
  5. stop reporting on functions that return non-promises
    • because, once again, this function does not return only a promise, so it should not be reported

Regarding the point 5: the rule appears to be enforcing three separate issues:

  • use async so that the promise-returning functions only throw synchronously (as the name implies)
  • specify the return type (as suggested by the docs)
  • do not use A | Promise<B> (as suggested by the fixer)

@bradzacher
Copy link
Member

remove this exception, report regardless of return type presence
because I don’t think inference should validate or invalidate this rule

In the vast, vast, vast majority of cases the developer intent is to just return a promise and not to have a hybrid return type - and if you asyncify the function then you get that exact behaviour - so the rule reporting is correct and guides the user towards better code.

But there are cases where you needs to intentionally NOT asyncify the function because you NEED a hybrid output. Instead of just relying on a disable comment here we chose to take the return type as explicit developer intent that a hybrid return type was desired.

So put another way:

  1. no explicit return type + hybrid return type = highly likely wanted just promise
  2. explicit return type + hybrid return type = definitely wanted a hybrid return type
  3. promise return type (with or without an explicit return type) = definitely wanted just promise

So the rule reports in (1) and (3) and not in (2).

@fregante
Copy link
Contributor Author

  • no explicit return type + hybrid return type = highly likely wanted just promise

Right, but that's what I meant in my last point, the rule is broad and it enforces something that is not in the explicit intent of the rule.

Even if you don't agree to change that, suggestions 1 and 3 still apply (error message + additional fixer)

Instead of just relying on a disable comment

Yeah, that's why it would be great to update the error message to suggest this. Most developers barely read the error messages, let alone googling the rule name to read its docs.

@bradzacher
Copy link
Member

stop reporting on functions that return non-promises
because, once again, this function does not return only a promise, so it should not be reported

Again I want to stress that in the vast, vast, vast majority of cases a user DOES NOT WANT A HYBRID RETURN TYPE and instead the hybrid return type is an inference error caused by a function that returns a plain and a promise value.

Eg cases like this:

function doSomethingAsync(): Promise<number>;

function doSomething(condition: boolean) {
  if (condition) {
    return 1;
  }
  return doSomethingAsync();
}

Generally the intent here is "this function returns Promise<number>" because you generally can't pass number | Promise<number> anywhere and you can't call promise methods on it. And thus generally speaking users want the Promise return type.

@bradzacher
Copy link
Member

My 2c is that

  • this is and "always"[1] has been the intended behaviour for many, many years
  • most users expect this case to be reported because most usecases don't want a hybrid return type.

So removing the behaviour goes against that established and expected practice.

I'd be okay with us updating the error message if the function has a hybrid return type to mention that an explicit annotation will silence the rule.


[1] iirc this case was added after the rule was first created but regardless it's near enough to "always".

@bradzacher bradzacher added accepting prs Go ahead, send a pull request that resolves this issue and removed triage Waiting for team members to take a look labels Jan 20, 2025
@github-actions github-actions bot added the locked due to age Please open a new issue if you'd like to say more. See https://typescript-eslint.io/contributing. label Apr 22, 2025
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 22, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
accepting prs Go ahead, send a pull request that resolves this issue bug Something isn't working locked due to age Please open a new issue if you'd like to say more. See https://typescript-eslint.io/contributing. package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin
Projects
None yet
2 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy