Skip to content

[node]: Allow timer functions to work with callbacks with a single unknown argument#54286

Merged
typescript-bot merged 1 commit into
DefinitelyTyped:masterfrom
birtles:fix-timers
Jul 8, 2021
Merged

[node]: Allow timer functions to work with callbacks with a single unknown argument#54286
typescript-bot merged 1 commit into
DefinitelyTyped:masterfrom
birtles:fix-timers

Conversation

@birtles

@birtles birtles commented Jul 5, 2021

Copy link
Copy Markdown
Contributor

Fixes #54285.

Please fill in this template.

Select one of these and delete the others:

If changing an existing definition:

@birtles birtles requested a review from peterblazejewicz as a code owner July 5, 2021 06:47
@typescript-bot

typescript-bot commented Jul 5, 2021

Copy link
Copy Markdown
Contributor

@birtles Thank you for submitting this PR!

This is a live comment which I will keep updated.

1 package in this PR

Code Reviews

Because this is a widely-used package, a DT maintainer will need to review it before it can be merged.

You can test the changes of this PR in the Playground.

Status

  • ✅ No merge conflicts
  • ✅ Continuous integration tests have passed
  • ✅ Most recent commit is approved by a DT maintainer

All of the items on the list are green. To merge, you need to post a comment including the string "Ready to merge" to bring in your changes.


Diagnostic Information: What the bot saw about this PR
{
  "type": "info",
  "now": "-",
  "pr_number": 54286,
  "author": "birtles",
  "headCommitOid": "376e4e828308661aa8512cff74cd926550b6c457",
  "lastPushDate": "2021-07-07T01:14:28.000Z",
  "lastActivityDate": "2021-07-08T23:42:03.000Z",
  "mergeOfferDate": "2021-07-08T17:38:29.000Z",
  "mergeRequestDate": "2021-07-08T23:42:03.000Z",
  "mergeRequestUser": "birtles",
  "hasMergeConflict": false,
  "isFirstContribution": false,
  "tooManyFiles": false,
  "popularityLevel": "Critical",
  "pkgInfo": [
    {
      "name": "node",
      "kind": "edit",
      "files": [
        {
          "path": "types/node/test/timers.ts",
          "kind": "test"
        },
        {
          "path": "types/node/timers.d.ts",
          "kind": "definition"
        }
      ],
      "owners": [
        "Microsoft",
        "DefinitelyTyped",
        "jkomyno",
        "alvis",
        "r3nya",
        "btoueg",
        "smac89",
        "touffy",
        "DeividasBakanas",
        "eyqs",
        "Hannes-Magnusson-CK",
        "KSXGitHub",
        "hoo29",
        "kjin",
        "ajafff",
        "islishude",
        "mwiktorczyk",
        "mohsen1",
        "n-e",
        "galkin",
        "parambirs",
        "eps1lon",
        "SimonSchick",
        "ThomasdenH",
        "WilcoBakker",
        "wwwy3y3",
        "samuela",
        "kuehlein",
        "bhongy",
        "chyzwar",
        "trivikr",
        "nguymin4",
        "yoursunny",
        "qwelias",
        "ExE-Boss",
        "Ryan-Willpower",
        "peterblazejewicz",
        "addaleax",
        "JasonHK",
        "victorperin",
        "ZYSzys"
      ],
      "addedOwners": [],
      "deletedOwners": [],
      "popularityLevel": "Critical"
    }
  ],
  "reviews": [
    {
      "type": "approved",
      "reviewer": "rbuckton",
      "date": "2021-07-08T17:37:55.000Z",
      "isMaintainer": true
    },
    {
      "type": "stale",
      "reviewer": "SimonSchick",
      "date": "2021-07-06T17:32:56.000Z",
      "abbrOid": "6be917a"
    }
  ],
  "mainBotCommentID": 873850293,
  "ciResult": "pass"
}

@typescript-bot

Copy link
Copy Markdown
Contributor

@KapitanOczywisty

KapitanOczywisty commented Jul 5, 2021

Copy link
Copy Markdown
Contributor

I feel like resolve should have optional argument (void default) - since you can leave it out, rather than adding unknown to setTimeout. Additionally type is always undefined so It might be counter-intuitive in some cases.

TBH I think that error produced by is correct and in this exact case user should use new promise variants from node v16, however I understand that this might be a problem of wider compatibility browser/node or v14/v16. Though I'm still convinced that promise is a problem, not a timers.

Edit:

Simple T = void fixes this problem.

declare interface PromiseConstructor {
    new <T = void>(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>;
}

Nope.

@birtles

birtles commented Jul 5, 2021

Copy link
Copy Markdown
Contributor Author

TBH I think that error produced by is correct and in this exact case user should use new promise variants from node v16, however I understand that this might be a problem of wider compatibility browser/node or v14/v16. Though I'm still convinced that promise is a problem, not a timers.

Yes, I agree. Like you my concern is we don't break code like await new Promise(resolve => setTimeout(resolve, 100)) since, even if there's a better way, code like that is so common we'll break nearly everyone updating to v16.

If we can get the T = void change landed for PromiseConstructor that sounds good. I wonder if there are any side effects of that we need to consider? It sounds like we'd be touching an even more core part of the typings so I have no idea who needs to sign off on that.

@KapitanOczywisty

Copy link
Copy Markdown
Contributor

code like that is so common we'll break nearly everyone updating to v16

Maintainers have probably some proven way to deal with such transitions. Maybe even including changed PromiseConstructor to v16 until typescript update would be properly distributed.

I wonder if there are any side effects of that we need to consider?

Don't think so, Promise.resolve is splitted into 2 variants, but I'd say it's for some legacy reasons.

@KapitanOczywisty

Copy link
Copy Markdown
Contributor

Some references: value argument in resolve cb was optional, but this was causing acceptance of undefined, so they've made argument required microsoft/TypeScript#39817 , but for some reason they didn't make T = void as default nor second signature. For me seems like that PR made "degradation" and v16 changes just makes this problem visible.

There was also issue microsoft/TypeScript#41497 but I think it was one big misunderstanding and nobody was rising setTimeout issue - since it wasn't present at that time.

@SimonSchick SimonSchick left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This makes sense to me, while it's unfortunate that we have to work around this this way, the solution looks OK (minus maybe my suggested change).

Changing the Promise type in core TS definitions is probably not going to happen and very out of scope of this issue.

Comment thread types/node/timers.d.ts Outdated
}

function setTimeout<TArgs extends any[]>(callback: (...args: TArgs) => void, ms?: number, ...args: TArgs): NodeJS.Timeout;
function setTimeout(callback: (args: unknown) => void): NodeJS.Timeout;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It might actually be better to use void rather than unknown.

Also please add a brief comment on why these rather weird overloads exist for future ref.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've updated the PR now with this feedback. I hope the comments make sense.

@KapitanOczywisty

KapitanOczywisty commented Jul 5, 2021

Copy link
Copy Markdown
Contributor

I have to agree with Simon. I was messing a bit with new Promise definition and type inference is not working as expected, so T = void is out of picture for now. TBH I was surprised that type inference in new Promise does not really work. microsoft/TypeScript#32254 (comment)

And change unknown to void fulfills my point about undefined, in this case void is more fitting.

@typescript-bot typescript-bot added the The CI failed When GH Actions fails label Jul 6, 2021
@typescript-bot

Copy link
Copy Markdown
Contributor

@birtles The CI build failed! Please review the logs for more information.

Once you've pushed the fixes, the build will automatically re-run. Thanks!

Note: builds which are failing do not end up on the list of PRs for the DT maintainers to review.

@birtles

birtles commented Jul 6, 2021

Copy link
Copy Markdown
Contributor Author

Apparently we can't use void here:

ERROR: 46:46  void-return  Use the `void` type for return types only. Otherwise, use `undefined`

Explanation here: https://github.com/Microsoft/dtslint/blob/master/docs/void-return.md

Should this really be:

function setTimeout(callback: (args: undefined) => void): NodeJS.Timeout;

?

@typescript-bot typescript-bot removed the The CI failed When GH Actions fails label Jul 6, 2021
@KapitanOczywisty

Copy link
Copy Markdown
Contributor

void is evidently supported in arguments... microsoft/TypeScript#27522 Rule might be out of date: https://github.com/microsoft/dtslint/blame/master/src/rules/voidReturnRule.ts There is issue touching this, but no real discussion: microsoft/dtslint#256

Maybe // tslint:disable-next-line: void-return in this case? https://grep.app/search?q=void-return&filter[repo][0]=DefinitelyTyped/DefinitelyTyped&filter[path][0]=types/

@birtles

birtles commented Jul 6, 2021

Copy link
Copy Markdown
Contributor Author

Maybe // tslint:disable-next-line: void-return in this case? https://grep.app/search?q=void-return&filter[repo][0]=DefinitelyTyped/DefinitelyTyped&filter[path][0]=types/

Thanks! Updated as suggested.

@typescript-bot

Copy link
Copy Markdown
Contributor

@SimonSchick Thank you for reviewing this PR! The author has pushed new commits since your last review. Could you take another look and submit a fresh review?

Comment thread types/node/timers.d.ts Outdated
function clearTimeout(timeoutId: NodeJS.Timeout): void;

function setInterval<TArgs extends any[]>(callback: (...args: TArgs) => void, ms?: number, ...args: TArgs): NodeJS.Timer;
// As with setTimeout, overload to allow setInterval to take a Promise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is excessive, can be reduced to // util.promisify no rest args compability

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sure!

@typescript-bot

Copy link
Copy Markdown
Contributor

@SimonSchick Thank you for reviewing this PR! The author has pushed new commits since your last review. Could you take another look and submit a fresh review?

@elibarzilay

Copy link
Copy Markdown
Contributor

Ping @rbuckton

@rbuckton rbuckton left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This looks fine. Supporting setInterval seems iffy, but doesn't hurt anything.

Comment thread types/node/timers.d.ts
function setInterval<TArgs extends any[]>(callback: (...args: TArgs) => void, ms?: number, ...args: TArgs): NodeJS.Timer;
// util.promisify no rest args compability
// tslint:disable-next-line void-return
function setInterval(callback: (args: void) => void, ms?: number): NodeJS.Timer;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not sure how useful this is for setInterval since you can only resolve a promise once.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Promisified setInterval returns an async iterator.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As far as wrapping it in a new Promise(...) goes I'd with setInterval, seems extremely niche.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure but there may be other circumstances apart from Promises where this comes up. This comment may be from a non-Promise situation judging by the function name.

I think it may be best to just merge this now to keep it consistent.

@typescript-bot typescript-bot added Maintainer Approved Self Merge This PR can now be self-merged by the PR author or an owner labels Jul 8, 2021
@birtles

birtles commented Jul 8, 2021

Copy link
Copy Markdown
Contributor Author

Ready to merge

@typescript-bot typescript-bot merged commit 3bf39c9 into DefinitelyTyped:master Jul 8, 2021
@typescript-bot

Copy link
Copy Markdown
Contributor

I just published @types/node@16.0.3 to npm.

Comment thread types/node/timers.d.ts
function setTimeout<TArgs extends any[]>(callback: (...args: TArgs) => void, ms?: number, ...args: TArgs): NodeJS.Timeout;
// util.promisify no rest args compability
// tslint:disable-next-line void-return
function setTimeout(callback: (args: void) => void): NodeJS.Timeout;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry, I know this PR has already been merged, but shouldn't this have the ms parameters as for setInterval?
I still have the Expected 3 arguments, but got 2 error for setTimeout but not for setInterval

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You're right. Good catch. CC @birtles

@SimonSchick Seems like tests are passing because of "dom" lib in tsconfig.json. How to test this properly?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It's kinda tricky, in CI we need to test it with the dom lib to ensure compatibility, you can always temporarily remove it test, add it back, I understand this is a tiny bit of churn but should be OK for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Critical package Maintainer Approved Self Merge This PR can now be self-merged by the PR author or an owner

Projects

None yet

7 participants