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

[@types/lodash] Infer lodash.throttle return type from 'leading' option #69504

Merged
merged 3 commits into from May 16, 2024

Conversation

dawaltconley
Copy link
Contributor

@dawaltconley dawaltconley commented May 3, 2024

This sets the throttle types to follow the same pattern as debounce (which it calls directly), accounting for the different default values (throttle has leading: true by default, debounce has leading: false).

I'm less familiar with how the fp packages are supposed to work, didn't see a way to pass options. I changed the lodash/fp/throttle function to return the same value as lodash/throttle with default options (DebounceFuncLeading).

Please fill in this template.

Select one of these and delete the others:

If changing an existing definition:

  • Provide a URL to documentation or source code which provides context for the suggested changes:
  • If this PR brings the type definitions up to date with a new version of the JS library, update the version number in the package.json.

@typescript-bot
Copy link
Contributor

typescript-bot commented May 3, 2024

@dawaltconley 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": 69504,
  "author": "dawaltconley",
  "headCommitOid": "4c69ba689494218c99c8390ce79c25ed6976fa22",
  "mergeBaseOid": "1243e1a4d11bb863290220ddb6fd678526bf4d47",
  "lastPushDate": "2024-05-03T00:48:47.000Z",
  "lastActivityDate": "2024-05-16T15:02:42.000Z",
  "mergeOfferDate": "2024-05-16T14:04:38.000Z",
  "mergeRequestDate": "2024-05-16T15:02:42.000Z",
  "mergeRequestUser": "dawaltconley",
  "hasMergeConflict": false,
  "isFirstContribution": false,
  "tooManyFiles": false,
  "hugeChange": false,
  "popularityLevel": "Critical",
  "pkgInfo": [
    {
      "name": "lodash",
      "kind": "edit",
      "files": [
        {
          "path": "types/lodash/common/function.d.ts",
          "kind": "definition"
        },
        {
          "path": "types/lodash/fp.d.ts",
          "kind": "definition"
        },
        {
          "path": "types/lodash/lodash-tests.ts",
          "kind": "test"
        }
      ],
      "owners": [
        "bczengel",
        "chrootsu",
        "aj-r",
        "e-cloud",
        "thorn0",
        "jtmthf",
        "DomiR",
        "WilliamChelman"
      ],
      "addedOwners": [],
      "deletedOwners": [],
      "popularityLevel": "Critical"
    }
  ],
  "reviews": [
    {
      "type": "approved",
      "reviewer": "sandersn",
      "date": "2024-05-16T14:04:01.000Z",
      "isMaintainer": true
    },
    {
      "type": "approved",
      "reviewer": "aj-r",
      "date": "2024-05-14T17:48:58.000Z",
      "isMaintainer": false
    }
  ],
  "mainBotCommentID": 2091965284,
  "ciResult": "pass"
}

@typescript-bot
Copy link
Contributor

🔔 @bczengel @chrootsu @aj-r @e-cloud @thorn0 @jtmthf @DomiR @WilliamChelman — please review this PR in the next few days. Be sure to explicitly select Approve or Request Changes in the GitHub UI so I know what's going on.

@typescript-bot typescript-bot moved this from Waiting for Code Reviews to Needs Maintainer Review in New Pull Request Status Board May 3, 2024
Copy link
Contributor

@aj-r aj-r left a comment

Choose a reason for hiding this comment

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

Thanks for the PR!

I'm pretty sure it's impossible to pass options to the FP version, so I think you are correct to always return the leading version.

@@ -1372,25 +1375,34 @@ declare module "../index" {
* @param options.trailing Specify invoking on the trailing edge of the timeout.
* @return Returns the new throttled function.
*/
throttle<T extends (...args: any) => any>(func: T, wait?: number, options?: ThrottleSettings): DebouncedFunc<T>;
throttle<T extends (...args: any) => any>(func: T, wait: number | undefined, options: ThrottleSettingsNoLeading): DebouncedFunc<T>;
throttle<T extends (...args: any) => any>(func: T, wait?: number, options?: ThrottleSettings): DebouncedFuncLeading<T>;
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a little awkward, since this overload will match if the caller passes a boolean variable (instead of the literal this for leading. In this case, we don't know if it's leading or not, so ideally the return type should include undefined just in case.

Is there a way to check if the leading field is unset? e.g.

ThrottleSettingsLeadingUnset extends ThrottleSettings {
  leading?: never; // or maybe undefined?
}

Then return DebouncedFuncLeading only if leading is unset or set to true.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point! As an extra complication, I just checked and apparently lodash treats an explicit { leading: undefined } the same as { leading: false }. So unset and undefined fields should be treated differently.

This is what I came up with, going to commit with tests but not sure if there's a way to handle this without some kind of union type. never didn't seem to work.

interface ThrottleSettings {
    leading?: boolean | undefined;
    trailing?: boolean | undefined;
}
type ThrottleSettingsLeading = (ThrottleSettings & { leading: true }) | Omit<ThrottleSettings, 'leading'>
interface LoDashStatic {
    throttle<T extends (...args: any) => any>(func: T, wait?: number, options?: ThrottleSettingsLeading): DebouncedFuncLeading<T>;
    throttle<T extends (...args: any) => any>(func: T, wait?: number, options?: ThrottleSettings): DebouncedFunc<T>;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Wow good find! In that case it might be nice to discourage people from setting the field to undefined since it almost certainly doesn't do what they expect. But I don't think there's a good way to do that (at least not better than what you have already) so I think we'll have to settle for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just pushed a commit to clarify the tests. Had the thought right after that it is possible to prohibit an explicit undefined here, while still making leading an optional property.

interface ThrottleSettings {
    leading: boolean;
    trailing?: boolean | undefined;
}
type ThrottleSettingsLeading = (ThrottleSettings & { leading: true }) | Omit<ThrottleSettings, 'leading'>
interface LoDashStatic {
    throttle<T extends (...args: any) => any>(func: T, wait?: number, options?: ThrottleSettingsLeading): DebouncedFuncLeading<T>;
    throttle<T extends (...args: any) => any>(func: T, wait?: number, options?: ThrottleSettings): DebouncedFunc<T>;
}

But I think that's a breaking change and I don't know if it's preferable anyway.

Copy link
Contributor

Choose a reason for hiding this comment

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

Pretty much every type change is a breaking change, due to the way types work. But I think that change still doesn't completely prevent explicit undefined, so it's debatable whether it's worth the potential breakage. (It prevents explicit undefined in object literals but not prebuilt objects. Maybe better than nothing?)

@@ -3660,21 +3660,27 @@ fp.now(); // $ExpectType number
leading: true,
trailing: false,
};
const optionsNoLeading: _.ThrottleSettingsNoLeading = {
Copy link
Contributor

Choose a reason for hiding this comment

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

It might be worth writing the options inline, instead of explicitly setting the type here, since most users won't set an explicit type, and that could mess with the overload selection.

_.throttle(func); // $ExpectType DebouncedFuncLeading<(a: number, b: string) => boolean>
_.throttle(func, 42); // $ExpectType DebouncedFuncLeading<(a: number, b: string) => boolean>
_.throttle(func, 42, options); // $ExpectType DebouncedFuncLeading<(a: number, b: string) => boolean>
_.throttle(func, 42, optionsNoLeading); // $ExpectType DebouncedFunc<(a: number, b: string) => boolean>
Copy link
Contributor

Choose a reason for hiding this comment

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

Add a test with an empty object literal ({}) for the options, and a test where leading has type boolean (or true|false).

@typescript-bot typescript-bot added the Revision needed This PR needs code changes before it can be merged. label May 3, 2024
@typescript-bot typescript-bot moved this from Needs Maintainer Review to Needs Author Action in New Pull Request Status Board May 3, 2024
@typescript-bot
Copy link
Contributor

@dawaltconley One or more reviewers has requested changes. Please address their comments. I'll be back once they sign off or you've pushed new commits. Thank you!

@typescript-bot typescript-bot removed the Revision needed This PR needs code changes before it can be merged. label May 3, 2024
@typescript-bot typescript-bot moved this from Needs Author Action to Waiting for Code Reviews in New Pull Request Status Board May 3, 2024
@typescript-bot
Copy link
Contributor

@aj-r 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?

@typescript-bot typescript-bot moved this from Waiting for Code Reviews to Needs Maintainer Review in New Pull Request Status Board May 3, 2024
};
const boolean: boolean = false;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: you might need false as boolean, otherwise TS will automatically narrow the type to false which is not what we want.

Copy link
Contributor

@aj-r aj-r left a comment

Choose a reason for hiding this comment

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

Looks great, just a minor tweak for readability

};
const boolean: boolean = false;
const maybeUndefined = (function (): true | undefined { return true })();
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar to above: a simpler way to do this might be something like:

const maybeUndefined: true | undefined = true as any;

@typescript-bot typescript-bot added the Owner Approved A listed owner of this package signed off on the pull request. label May 12, 2024
_.throttle(func, 42, { leading: false }); // $ExpectType DebouncedFunc<(a: number, b: string) => boolean>
_.throttle(func, 42, { leading: undefined }); // $ExpectType DebouncedFunc<(a: number, b: string) => boolean>
_.throttle(func, 42, { leading: boolean }); // $ExpectType DebouncedFunc<(a: number, b: string) => boolean>
_.throttle(func, 42, { leading: maybeUndefined }); // $ExpectType DebouncedFunc<(a: number, b: string) => boolean>
Copy link
Contributor

Choose a reason for hiding this comment

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

Also add a comment explaining why we should NOT expect DebouncedFuncLeading here, since this is probably surprising to everyone.

@typescript-bot typescript-bot added Revision needed This PR needs code changes before it can be merged. and removed Owner Approved A listed owner of this package signed off on the pull request. labels May 12, 2024
@typescript-bot
Copy link
Contributor

@dawaltconley One or more reviewers has requested changes. Please address their comments. I'll be back once they sign off or you've pushed new commits. Thank you!

@typescript-bot typescript-bot moved this from Needs Maintainer Review to Needs Author Action in New Pull Request Status Board May 12, 2024
Explicitly casts ambiguous values to avoid type narrowing. Also adds explanatory comments to tests with an explicit
undefined leading.
@typescript-bot typescript-bot removed the Revision needed This PR needs code changes before it can be merged. label May 13, 2024
@typescript-bot typescript-bot moved this from Needs Author Action to Waiting for Code Reviews in New Pull Request Status Board May 13, 2024
@typescript-bot
Copy link
Contributor

@aj-r 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?

@typescript-bot typescript-bot moved this from Waiting for Code Reviews to Needs Maintainer Review in New Pull Request Status Board May 13, 2024
Copy link
Contributor

@aj-r aj-r left a comment

Choose a reason for hiding this comment

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

Looks great!

@typescript-bot typescript-bot added the Owner Approved A listed owner of this package signed off on the pull request. label May 13, 2024
@typescript-bot typescript-bot removed the Owner Approved A listed owner of this package signed off on the pull request. label May 13, 2024
@typescript-bot
Copy link
Contributor

typescript-bot commented May 14, 2024

Re-ping @bczengel, @chrootsu, @e-cloud, @thorn0, @jtmthf, @DomiR, @WilliamChelman:

This PR has been out for over a week, yet I haven't seen any reviews.

Could someone please give it some attention? Thanks!

@typescript-bot typescript-bot added the Unreviewed No one showed up to review this PR, so it'll be reviewed by a DT maintainer. label May 14, 2024
Copy link
Contributor

@aj-r aj-r left a comment

Choose a reason for hiding this comment

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

I'm not sure why typescript-bot thinks there are no reviews...

@typescript-bot typescript-bot added the Owner Approved A listed owner of this package signed off on the pull request. label May 14, 2024
@typescript-bot typescript-bot added Maintainer Approved Self Merge This PR can now be self-merged by the PR author or an owner and removed Unreviewed No one showed up to review this PR, so it'll be reviewed by a DT maintainer. labels May 16, 2024
@typescript-bot
Copy link
Contributor

@dawaltconley: Everything looks good here. I am ready to merge this PR (at 4c69ba6) on your behalf whenever you think it's ready.

If you'd like that to happen, please post a comment saying:

Ready to merge

and I'll merge this PR almost instantly. Thanks for helping out! ❤️

(@bczengel, @chrootsu, @aj-r, @e-cloud, @thorn0, @jtmthf, @DomiR, @WilliamChelman: you can do this too.)

@typescript-bot typescript-bot moved this from Needs Maintainer Review to Waiting for Author to Merge in New Pull Request Status Board May 16, 2024
@dawaltconley
Copy link
Contributor Author

Ready to merge

@typescript-bot typescript-bot moved this from Waiting for Author to Merge to Recently Merged in New Pull Request Status Board May 16, 2024
@typescript-bot typescript-bot merged commit 91003f3 into DefinitelyTyped:master May 16, 2024
7 checks passed
@typescript-bot typescript-bot removed this from Recently Merged in New Pull Request Status Board May 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Critical package Maintainer Approved Owner Approved A listed owner of this package signed off on the pull request. Self Merge This PR can now be self-merged by the PR author or an owner
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants