-
Notifications
You must be signed in to change notification settings - Fork 29.9k
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3660,21 +3660,27 @@ fp.now(); // $ExpectType number | |
leading: true, | ||
trailing: false, | ||
}; | ||
const optionsNoLeading: _.ThrottleSettingsNoLeading = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
leading: false, | ||
}; | ||
|
||
const func = (a: number, b: string): boolean => true; | ||
|
||
_.throttle(func); // $ExpectType DebouncedFunc<(a: number, b: string) => boolean> | ||
_.throttle(func, 42); // $ExpectType DebouncedFunc<(a: number, b: string) => boolean> | ||
_.throttle(func, 42, options); // $ExpectType DebouncedFunc<(a: number, b: string) => boolean> | ||
_(func).throttle(); // $ExpectType Function<DebouncedFunc<(a: number, b: string) => boolean>> | ||
_(func).throttle(42); // $ExpectType Function<DebouncedFunc<(a: number, b: string) => boolean>> | ||
_(func).throttle(42, options); // $ExpectType Function<DebouncedFunc<(a: number, b: string) => boolean>> | ||
_.chain(func).throttle(); // $ExpectType FunctionChain<DebouncedFunc<(a: number, b: string) => boolean>> | ||
_.chain(func).throttle(42); // $ExpectType FunctionChain<DebouncedFunc<(a: number, b: string) => boolean>> | ||
_.chain(func).throttle(42, options); // $ExpectType FunctionChain<DebouncedFunc<(a: number, b: string) => boolean>> | ||
|
||
fp.throttle(42, func); // $ExpectType DebouncedFunc<(a: number, b: string) => boolean> | ||
fp.throttle(42)(func); // $ExpectType DebouncedFunc<(a: number, b: string) => boolean> | ||
_.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> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a test with an empty object literal ( |
||
_(func).throttle(); // $ExpectType Function<DebouncedFuncLeading<(a: number, b: string) => boolean>> | ||
_(func).throttle(42); // $ExpectType Function<DebouncedFuncLeading<(a: number, b: string) => boolean>> | ||
_(func).throttle(42, options); // $ExpectType Function<DebouncedFuncLeading<(a: number, b: string) => boolean>> | ||
_(func).throttle(42, optionsNoLeading); // $ExpectType Function<DebouncedFunc<(a: number, b: string) => boolean>> | ||
_.chain(func).throttle(); // $ExpectType FunctionChain<DebouncedFuncLeading<(a: number, b: string) => boolean>> | ||
_.chain(func).throttle(42); // $ExpectType FunctionChain<DebouncedFuncLeading<(a: number, b: string) => boolean>> | ||
_.chain(func).throttle(42, options); // $ExpectType FunctionChain<DebouncedFuncLeading<(a: number, b: string) => boolean>> | ||
_.chain(func).throttle(42, optionsNoLeading); // $ExpectType FunctionChain<DebouncedFunc<(a: number, b: string) => boolean>> | ||
|
||
fp.throttle(42, func); // $ExpectType DebouncedFuncLeading<(a: number, b: string) => boolean> | ||
fp.throttle(42)(func); // $ExpectType DebouncedFuncLeading<(a: number, b: string) => boolean> | ||
} | ||
|
||
// _.unary | ||
|
There was a problem hiding this comment.
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
forleading
. 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.Then return
DebouncedFuncLeading
only ifleading
is unset or set to true.There was a problem hiding this comment.
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.There was a problem hiding this comment.
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.There was a problem hiding this comment.
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.But I think that's a breaking change and I don't know if it's preferable anyway.
There was a problem hiding this comment.
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?)