Skip to content

Deeply inferred function argument's type wrongly cannot be assigned to a matching generic parameter #23922

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
Raiondesu opened this issue May 6, 2018 · 2 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@Raiondesu
Copy link

Raiondesu commented May 6, 2018

TypeScript Version: 2.9.0-dev.20180505

Search Terms:
generic function parameter is not assignable
deep type wrongly cannot be assigned to generic
function generic type is not inferred correctly

Code

Let's say we have a basic generic function that checks its arguments and does something to them according to a strategy function if both "exist":

function baseMerge<T>(parent: T, child: T, strategy: (parent: T, child: T) => T) {
  if (parent != undefined && child != undefined) {
    return strategy(parent, child)
  } else if (parent == undefined && child) {
    return child
  } else if (child == undefined && parent) {
    return parent
  } else {
    return undefined
  }
}

Then, trying to implement a strategy with a generic argument:

const pipe = <F extends (...args: any[]) => any[]>(parent: F, child: F) => baseMerge<F>(
  parent,
  child,
  (p, c) => (...args) => c(...p(...args)) // Here, the strategy just pipes the two array returning functions, one into the other
)

Expected behavior:

The code just compiles as usual.

Actual behavior:

Error on this line:

(p, c) => (...args) => c(...p(...args))

Type (...args: any[]) => any[] is not assignable to type F

Only a strict typecast fixes this:

(p, c) => ((...args) => c(...p(...args))) as F

which seems wrongly explicit to me, as the assigned type is exactly the same as the inferred one.

It can be viewed as unexpected behavior as in almost exactly the same use-case (outside of the function that wraps the generic) the issue is absent:

type f = (...args: any[]) => any[];
type Pipe<F extends f> = (p: F, c: F) => F;

// No error here, surprisingly
const a: Pipe<f> = (p: f, c: f) => (...args) => c(...p(...args))

Playground Link:

https://www.typescriptlang.org/play/#src=function%20baseMerge%3CT%3E(parent%3A%20T%2C%20child%3A%20T%2C%20strategy%3A%20(parent%3A%20T%2C%20child%3A%20T)%20%3D%3E%20T)%20%7B%0D%0A%20%20if%20(parent%20!%3D%20undefined%20%26%26%20child%20!%3D%20undefined)%20%7B%0D%0A%20%20%20%20return%20strategy(parent%2C%20child)%0D%0A%20%20%7D%20else%20if%20(parent%20%3D%3D%20undefined%20%26%26%20child)%20%7B%0D%0A%20%20%20%20return%20child%0D%0A%20%20%7D%20else%20if%20(child%20%3D%3D%20undefined%20%26%26%20parent)%20%7B%0D%0A%20%20%20%20return%20parent%0D%0A%20%20%7D%20else%20%7B%0D%0A%20%20%20%20return%20undefined%0D%0A%20%20%7D%0D%0A%7D%0D%0A%0D%0Aconst%20pipe%20%3D%20%3CF%20extends%20(...args%3A%20any%5B%5D)%20%3D%3E%20any%5B%5D%3E(parent%3A%20F%2C%20child%3A%20F)%20%3D%3E%20baseMerge%3CF%3E(%0D%0A%20%20parent%2C%0D%0A%20%20child%2C%0D%0A%20%20(p%2C%20c)%20%3D%3E%20(...args)%20%3D%3E%20c(...p(...args))%0D%0A)%0D%0A%0D%0A%0D%0A%2F%2F%20No%20error%20here%2C%20however%0D%0Atype%20f%20%3D%20(...args%3A%20any%5B%5D)%20%3D%3E%20any%5B%5D%3B%0D%0Atype%20aPipe%3CF%20extends%20f%20%3D%20f%3E%20%3D%20(p%3A%20F%2C%20c%3A%20F)%20%3D%3E%20F%3B%0D%0A%0D%0Aconst%20a%3A%20aPipe%20%3D%20(p%3A%20f%2C%20c%3A%20f)%20%3D%3E%20(...args)%20%3D%3E%20c(...p(...args))%0D%0A

Related Issues:
#23705 seems related to me. But as it's not quite the same and the expected result is different (success instead of an error), I decided to create a new one. With a playground. ^_^

Unrelated Issues:
This issue DOES NOT seem to be related to #23763, #19388, #17071 and #13442 (as one might think at first) due to the absence of Partial<T> (inferred type and generic type are exactly the same in my case) as well as wrong assumptions about type inference.

P.S. Tried as hard as I could to search for duplicates and alternate solutions. Found none.

@Raiondesu Raiondesu changed the title Deeply infered function argument's type wrongly cannot be assigned to a matching generic parameter Deeply inferred function argument's type wrongly cannot be assigned to a matching generic parameter May 6, 2018
@mhegazy
Copy link
Contributor

mhegazy commented Jul 18, 2018

Generic type F is assignable to its constraint (...args: any[]) => any[], but the opposite is not true. consider calling your function pipe with { (...args: any[]) => any; prop: number; }.

@mhegazy mhegazy added the Working as Intended The behavior described is the intended behavior; this is not a bug label Jul 18, 2018
@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants