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

Compose function can not deduce generic types of arguments if compose function is defined right to left #31738

Closed
kasperpeulen opened this issue Jun 3, 2019 · 1 comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@kasperpeulen
Copy link

TypeScript Version: 3.6.0-dev.20190603

Search Terms:
Compose doesn't infer generic types of arguments.

Code

function compose<F, G, R>(f: (x: F) => R, g: (y: G) => F): (y: G) => R {
  return x => f(g(x));
}

function toArray<T extends any>(element: T): T[] {
  return [element];
}

const doubleArray = compose(
  toArray,
  toArray,
)(1);
(11,3): error TS2345: Argument of type '<T extends any>(element: T) => T[]' is not assignable to parameter of type '(y: unknown) => T'.
  Type 'unknown[]' is not assignable to type 'T'.
    'unknown[]' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'any'.

Expected behavior:
I expect doubleArray to be inferred as number[][].
Actual behavior:
It is inferred as unknown.

I'm also not sure how to help typescript, to infer this correctly, for example, this doesn't work:

const doubleArray = compose(
  toArray<number>,
  toArray<number>,
)(1);

Playground Link:
Playground

Related Issues:
#10247

Interestingly, if we define compose from left to right, instead of the standard mathematical composition, it actually works:

function compose<F, G, R>(f: (x: F) => G, g: (y: G) => R): (x: F) => R {
  return x => g(f(x));
}

Playground

I choose a very generic example, but the code that I actually try to type are HOC with react.

For example:

export const Container = compose(
  connect(...),
  withStyles(...),
  withLoader(LoaderComponent),
  withTranslations
)(Component);

This code doesn't get inferred correctly, and also here, with the left to right compose function it does get inferred.

@jack-williams
Copy link
Collaborator

I think this might be a design limitation. See this comment from the H-O inference PR: #30215

The above algorithm is not a complete unification algorithm and it is by no means perfect. In particular, it only delivers the desired outcome when types flow from left to right. However, this has always been the case for type argument inference in TypeScript, and it has the highly desired attribute of working well as code is being typed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

4 participants