Skip to content

Function argument type inference in recursively modified types #50468

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

Closed
Azarattum opened this issue Aug 26, 2022 · 2 comments
Closed

Function argument type inference in recursively modified types #50468

Azarattum opened this issue Aug 26, 2022 · 2 comments
Labels
Duplicate An existing issue was already created

Comments

@Azarattum
Copy link

Bug Report

Whenever we use a recursive generic to alter an array of functions, then use this type as a function's arguments, parameter arrow functions do not have their arguments inferred by TypeScript.

I've been trying to get this properly working to implement a pipe typings (related #30370, #25660). Which lead me to investigate why the arguments in the suggested solution are not inferred. I nailed this down to recursion type modification and made a minimal reproduction (see the code below).

If this is solved the mentioned above issues would also resolve (and probably more).

🔎 Search Terms

recursion, flow, pipe, chain, inference

🕗 Version & Regression Information

  • The issue is present since version 4.1.5 (playground) when circular references were introduced that made possible to implement recursions. And still preset until 4.8.0-beta (playground).

💻 Code

Playground link with relevant code

/** Recursively replaces all items in array T with U */
type RecursiveReplace<T, U> =
    T extends [any, ...infer Tail]
    ? [U, ...RecursiveReplace<Tail, U>]
    : T;

/** Example of recursive replacement */
type Example = RecursiveReplace<[1,2,3], 3>
//   ^? type Example = [3, 3, 3]

/** This should transform any function signatures passed to it to `(_: number) => void` */
function test<T extends any[]>(..._: RecursiveReplace<T, (_: number) => void>) {}

/** This works fine */
test(
    (_: number) => {}
)

/** TypeScript complains about the wrong signature. This is OK too */
test(
    (_: string) => {} // ERROR: Type 'number' is not assignable to type 'string'
)

/** ------------------------------------------ ↓ THE BUG ↓ ------------------------------------------- */ 
/** TypeScript complains about an implicit `any`. However the argument should be inferred as `number`! */
test(
    (_) => {},
//   ^? (parameter) _: any
);
/** -------------------------------------------------------------------------------------------------- */

🙁 Actual behavior

Recursive modification of type signatures breaks argument inference in functions.

🙂 Expected behavior

Function parameters should be properly inferred even after type signature was recursively modified.

@RyanCavanaugh
Copy link
Member

If you make the example need generics in the first place, it quickly becomes apparent that you need an unbounded number of inference rounds to type these things properly -- see #30134

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Aug 26, 2022
@typescript-bot
Copy link
Collaborator

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants