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

Presence of a binding pattern changes inference for callback function parameters #43605

Closed
yifanwww opened this issue Apr 9, 2021 · 4 comments · Fixed by #45719 or #49086
Closed

Presence of a binding pattern changes inference for callback function parameters #43605

yifanwww opened this issue Apr 9, 2021 · 4 comments · Fixed by #45719 or #49086
Assignees
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue

Comments

@yifanwww
Copy link

yifanwww commented Apr 9, 2021

Bug Report

🕗 Version & Regression Information

This bugs occurs in typescript 4.2.3, 4.2.4 and 4.3.0-beta, I didn't test the older typescripts.

⏯ Playground Link

Playground link with relevant code

💻 Code

// type definitions simplified from `@reduxjs/toolkit`, `react` and `react-redux`

type Dispatch<A = { type: any; [extraProps: string]: any }> = { <T extends A>(action: T): T };
declare function useMemo<T>(factory: () => T, deps: ReadonlyArray<any> | undefined): T;
declare function useDispatch<TDispatch = Dispatch<any>>(): TDispatch;

// IDestructuring & Destructuring

type IFuncs = { readonly [key: string]: (...p: any) => void };

type IDestructuring<T extends IFuncs> = { readonly [key in keyof T]?: (...p: Parameters<T[key]>) => void };

type Destructuring<T extends IFuncs, U extends IDestructuring<T>> = (dispatch: Dispatch<any>, funcs: T) => U;

// functions

const funcs1 = {
    funcA: (a: boolean): void => {},
    funcB: (b: string, bb: string): void => {},
    funcC: (c: number, cc: number, ccc: boolean): void => {},
};
const funcs2 = {
    funcD: (d: boolean): void => {},
    funcE: (e: string, ee: string): void => {},
    funcF: (f: number, ff: number, fff: boolean): void => {},
};

type TFuncs1 = typeof funcs1;
type TFuncs2 = typeof funcs2;

// react hooks

function useReduxDispatch1<T extends IDestructuring<TFuncs1>>(destructuring: Destructuring<TFuncs1, T>): T {
    return useMemo(() => ({ ...destructuring(useDispatch(), funcs1) }), []);
}

function useReduxDispatch2<T1 extends IDestructuring<TFuncs1>, T2 extends IDestructuring<TFuncs2>>(
    destructuring1: Destructuring<TFuncs1, T1>,
    destructuring2: Destructuring<TFuncs2, T2>,
): T1 & T2 {
    return useMemo(() => ({ ...destructuring1(useDispatch(), funcs1), ...destructuring2(useDispatch(), funcs2) }), []);
}

// test examples

useReduxDispatch1(
    (d, f) => ({ funcA: (...p) => d(f.funcA(...p)), funcC: (...p) => d(f.funcC(...p)) })
);

const _ = useReduxDispatch1(
    (d, f) => ({ funcA: (...p) => d(f.funcA(...p)), funcB: (...p) => d(f.funcB(...p)) })
);

const { funcA, funcB, funcC } = useReduxDispatch1(
    (d, f) => ({ funcA: (...p) => d(f.funcB(...p)), funcB: (...p) => d(f.funcB(...p)), funcC: (...p) => d(f.funcC(...p)) })
);

useReduxDispatch2(
    (d, f) => ({ funcB: (...p) => d(f.funcB(...p)), funcC: (...p) => d(f.funcC(...p)) }),
    (d, f) => ({ funcD: (...p) => d(f.funcD(...p)), funcF: (...p) => d(f.funcF(...p)) }),
);

const { funcA: _funcA, funcC: _funcC, funcE, funcF } = useReduxDispatch2(
    (d, f) => ({ funcA: (...p) => d(f.funcA(...p)), funcC: (...p) => d(f.funcC(...p)) }),
    (d, f) => ({ funcE: (...p) => d(f.funcE(...p)), funcF: (...p) => d(f.funcF(...p)) }),
);

🙁 Actual behavior

In the function call on line 55, typescript cannot infer the parameters of array functions funcA, funcB and funcC, actually it thinks the parameters ...args is any[].

But this bugs doesn't occur in function calls on line 46, 50, 58 and 63.

🙂 Expected behavior

No compilation errors.

Typescript should infer the parameters ...args of array function funcA, funcB and funcC correctly.

@andrewbranch
Copy link
Member

Slightly reduced, cleaned up, and emphasizing the nature of the bug: playground

@andrewbranch andrewbranch changed the title Typescript fails to infer the parameters of functions Presence of a binding pattern changes inference for callback function parameters Apr 9, 2021
@andrewbranch andrewbranch added the Bug A bug in TypeScript label Apr 9, 2021
@andrewbranch andrewbranch self-assigned this Apr 9, 2021
@andrewbranch andrewbranch added this to the 4.4.0 milestone Apr 9, 2021
@andrewbranch
Copy link
Member

Moving to 4.5 since the fix will probably be a breaking change

@andrewbranch andrewbranch added the Breaking Change Would introduce errors in existing code label Sep 2, 2021
@andrewbranch andrewbranch added Working as Intended The behavior described is the intended behavior; this is not a bug and removed Bug A bug in TypeScript Breaking Change Would introduce errors in existing code labels Sep 2, 2021
@andrewbranch andrewbranch removed their assignment Sep 2, 2021
@andrewbranch andrewbranch removed this from the TypeScript 4.5.0 milestone Sep 2, 2021
@andrewbranch
Copy link
Member

Looking at this again, it is very weird, but it is expected behavior. See #39081 for some discussion. The real problem is that U in Destructuring has no inference target aside from the return type, so there is simply no inference source aside from the binding pattern when used. As #39081 mentions, you could get around this by giving T in useReduxDispatch1 a default. However, the more I look at this example, the more it seems like the root cause of these poor inferences is that nothing here needs to be generic; return type inference is rarely meaningful, and it’s apparently not meaningful here.

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@andrewbranch andrewbranch added Bug A bug in TypeScript and removed Working as Intended The behavior described is the intended behavior; this is not a bug Fix Available A PR has been opened for this issue labels Sep 22, 2021
@andrewbranch andrewbranch reopened this Sep 22, 2021
@andrewbranch andrewbranch added this to the TypeScript 4.6.0 milestone Sep 22, 2021
@andrewbranch andrewbranch self-assigned this Sep 22, 2021
@RyanCavanaugh RyanCavanaugh removed this from the TypeScript 4.6.0 milestone Feb 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue
Projects
None yet
4 participants