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

Can not call a generic function as an call "effect" (pattern in in redux-saga) #40798

Closed
kasperpeulen opened this issue Sep 27, 2020 · 5 comments
Labels
Duplicate An existing issue was already created

Comments

@kasperpeulen
Copy link

TypeScript Version: 4.0.2

Code

interface Foo {
  bla: string;
}

declare function map<T, R>(value: T[], transform: (t: T) => R): R[];

declare const foos: Foo[];

declare function call<Fn extends (...args: any[]) => any>(
  fn: Fn,
  ...args: Parameters<Fn>
): ReturnType<Fn>;

const a = call(map, foos, (foo: Foo) => foo.bla);

Expected behavior:
I expect this to give no compile errors.

Actual behavior:

Argument of type '(foo: Foo) => string' is not assignable to parameter of type '(t: unknown) => unknown'. Types of parameters 'foo' and 't' are incompatible. Type 'unknown' is not assignable to type 'Foo'

Playground Link:
Playground

@jack-williams
Copy link
Collaborator

jack-williams commented Sep 27, 2020

This falls largely under the same design limitation as described here: #31811.

When using generics a good guideline to follow is to push generic parameters down such that they quantifier over as little structure as necessary. I think the best way to write this is:

declare function call<P extends unknown[], R>(
  fn: (...args: P) => R,
  ...args: P
): R;

@MartinJohns
Copy link
Contributor

@jack-williams was a minute faster than me. :-D

#38964 and #37181 are also related.

@kasperpeulen
Copy link
Author

kasperpeulen commented Sep 28, 2020

@jack-williams Ah that is interesting thanks!

The next step would be if the callback (the third argument), could be inferred from the first two arguments.

interface Foo { bla: string; }
declare function map<T, R>(value: T[], transform: (t: T) => R): R[];
declare const foos: Foo[];

declare function call<P extends unknown[], R>(
  fn: (...args: P) => R,
  ...args: P
): R;

// Object is of type 'unknown'.    vvv
const a = call(map, foos, (foo) => foo.bla);

This fails. With the error in the comment. However, if I write it like this:

declare function call<X, P extends unknown[], R>(
  fn: (x: X, ...args: P) => R,
  x: X,
  ...args: P
): R;

// Compiles, but a inferred as unknown[]
const a = call(map, foos, (foo) => foo.bla);

It does compile, if I hover over foo it is of type Foo, but a is inferred as unknown[]. Is this a "legit" bug?

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Sep 28, 2020
@RyanCavanaugh
Copy link
Member

Gonna have to link #30134 for that one

@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

5 participants