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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions types/lodash/common/function.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1355,6 +1355,7 @@ declare module "../index" {
*/
trailing?: boolean | undefined;
}
type ThrottleSettingsLeading = (ThrottleSettings & { leading: true }) | Omit<ThrottleSettings, 'leading'>
interface LoDashStatic {
/**
* Creates a throttled function that only invokes func at most once per every wait milliseconds. The throttled
Expand All @@ -1372,12 +1373,17 @@ 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?: ThrottleSettingsLeading): DebouncedFuncLeading<T>;
throttle<T extends (...args: any) => any>(func: T, wait?: number, options?: ThrottleSettings): DebouncedFunc<T>;
}
interface Function<T extends (...args: any) => any> {
/**
* @see _.throttle
*/
throttle(
wait?: number,
options?: ThrottleSettingsLeading
): T extends (...args: any[]) => any ? Function<DebouncedFuncLeading<T>> : never;
throttle(
wait?: number,
options?: ThrottleSettings
Expand All @@ -1387,6 +1393,10 @@ declare module "../index" {
/**
* @see _.throttle
*/
throttle(
wait?: number,
options?: ThrottleSettingsLeading
): T extends (...args: any[]) => any ? FunctionChain<DebouncedFuncLeading<T>> : never;
throttle(
wait?: number,
options?: ThrottleSettings
Expand Down
6 changes: 3 additions & 3 deletions types/lodash/fp.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4173,10 +4173,10 @@ declare namespace _ {
interface LodashThrottle {
(wait: number): LodashThrottle1x1;
<T extends (...args: any) => any>(wait: lodash.__, func: T): LodashThrottle1x2<T>;
<T extends (...args: any) => any>(wait: number, func: T): lodash.DebouncedFunc<T>;
<T extends (...args: any) => any>(wait: number, func: T): lodash.DebouncedFuncLeading<T>;
}
type LodashThrottle1x1 = <T extends (...args: any) => any>(func: T) => lodash.DebouncedFunc<T>;
type LodashThrottle1x2<T extends (...args: any) => any> = (wait: number) => lodash.DebouncedFunc<T>;
type LodashThrottle1x1 = <T extends (...args: any) => any>(func: T) => lodash.DebouncedFuncLeading<T>;
type LodashThrottle1x2<T extends (...args: any) => any> = (wait: number) => lodash.DebouncedFuncLeading<T>;
interface LodashThru {
<T, TResult>(interceptor: (value: T) => TResult): LodashThru1x1<T, TResult>;
<T>(interceptor: lodash.__, value: T): LodashThru1x2<T>;
Expand Down
63 changes: 50 additions & 13 deletions types/lodash/lodash-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3656,25 +3656,62 @@ fp.now(); // $ExpectType number

// _.throttle
{
const options: _.ThrottleSettings = {
const optionsLeading: _.ThrottleSettingsLeading = {
leading: true,
trailing: false,
}
const optionsNoLeading: _.ThrottleSettings = {
leading: false,
};

const optionsAmbiguous = {
leading: true as boolean,
};
const boolean = false as boolean; // $ExpectType boolean
const maybeUndefined = true as true | undefined; // $ExpectType true | undefined

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, {}); // $ExpectType DebouncedFuncLeading<(a: number, b: string) => boolean>
_.throttle(func, 42, { leading: true, trailing: false }); // $ExpectType DebouncedFuncLeading<(a: number, b: string) => boolean>
_.throttle(func, 42, { leading: false }); // $ExpectType DebouncedFunc<(a: number, b: string) => boolean>
_.throttle(func, 42, { leading: boolean }); // $ExpectType DebouncedFunc<(a: number, b: string) => boolean>
_.throttle(func, 42, optionsAmbiguous); // $ExpectType DebouncedFunc<(a: number, b: string) => boolean>

_(func).throttle(); // $ExpectType Function<DebouncedFuncLeading<(a: number, b: string) => boolean>>
_(func).throttle(42); // $ExpectType Function<DebouncedFuncLeading<(a: number, b: string) => boolean>>
_(func).throttle(42, {}); // $ExpectType Function<DebouncedFuncLeading<(a: number, b: string) => boolean>>
_(func).throttle(42, { leading: true, trailing: false }); // $ExpectType Function<DebouncedFuncLeading<(a: number, b: string) => boolean>>
_(func).throttle(42, { leading: false }); // $ExpectType Function<DebouncedFunc<(a: number, b: string) => boolean>>
_(func).throttle(42, { leading: boolean }); // $ExpectType Function<DebouncedFunc<(a: number, b: string) => boolean>>
_(func).throttle(42, optionsAmbiguous); // $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, {}); // $ExpectType FunctionChain<DebouncedFuncLeading<(a: number, b: string) => boolean>>
_.chain(func).throttle(42, { leading: true, trailing: false }); // $ExpectType FunctionChain<DebouncedFuncLeading<(a: number, b: string) => boolean>>
_.chain(func).throttle(42, { leading: false }); // $ExpectType FunctionChain<DebouncedFunc<(a: number, b: string) => boolean>>
_.chain(func).throttle(42, { leading: boolean }); // $ExpectType FunctionChain<DebouncedFunc<(a: number, b: string) => boolean>>
_.chain(func).throttle(42, optionsAmbiguous); // $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>

// _.throttle treats an explicit `leading: undefined` the same as `leading: false`
const badOptionsLeading: _.ThrottleSettingsLeading = {
// @ts-expect-error explicit undefined is not allowed here
leading: undefined,
};

// any invokation where leading might be set as undefined should return DebouncedFunc, not DebouncedFuncLeading
_.throttle(func, 42, { leading: undefined }); // $ExpectType DebouncedFunc<(a: number, b: string) => boolean>
_.throttle(func, 42, { leading: maybeUndefined }); // $ExpectType DebouncedFunc<(a: number, b: string) => boolean>
_.chain(func).throttle(42, { leading: undefined }); // $ExpectType FunctionChain<DebouncedFunc<(a: number, b: string) => boolean>>
_.chain(func).throttle(42, { leading: maybeUndefined }); // $ExpectType FunctionChain<DebouncedFunc<(a: number, b: string) => boolean>>
_(func).throttle(42, { leading: undefined }); // $ExpectType Function<DebouncedFunc<(a: number, b: string) => boolean>>
_(func).throttle(42, { leading: maybeUndefined }); // $ExpectType Function<DebouncedFunc<(a: number, b: string) => boolean>>
}

// _.unary
Expand Down