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

Supporting generic type inference over the other higher-order functions #9366

Closed
falsandtru opened this issue Jun 26, 2016 · 14 comments · Fixed by #30215
Closed

Supporting generic type inference over the other higher-order functions #9366

falsandtru opened this issue Jun 26, 2016 · 14 comments · Fixed by #30215
Labels
Effort: Difficult Good luck. Fixed A PR has been merged for this issue Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript

Comments

@falsandtru
Copy link
Contributor

falsandtru commented Jun 26, 2016

Currently, TypeScript infers generic types when referring its type immediately. Its behavior is undesirable for using higher-order functions. The type inference of generics should delay until it will be called.

function flip<a, b, c>(f: (a: a, b: b) => c): (b: b, a: a) => c {
  return (b: b, a: a) => f(a, b);
}
function zip<T, U>(x: T, y: U): [T, U] {
  return [x, y];
}
var expected: <T, U>(y: U, x: T) => [T, U] = flip(zip);
var actual: (y: {}, x: {}) => [{}, {}] = flip(zip);
@DanielRosenwasser DanielRosenwasser added Suggestion An idea for TypeScript Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Effort: Difficult Good luck. labels Jun 30, 2016
@falsandtru falsandtru changed the title Supporting generic type inference over the other higher functions Supporting generic type inference over the other higher-order functions Jul 4, 2016
@fdecampredon
Copy link

this issue is really problematic for functional paradigm users :

const map = <T, U>(transform: (t: T) => U) =>
    (arr: T[]) => arr.map(transform)

const identity = <T>(t: T) => t;

const identityStr = (t: string) => t;


const arr: string[] = map(identityStr)(['a']);
const arr1: string[] = map(identity)(['a']); // Type '{}[]' is not assignable to type 'string[]'.

@DanielRosenwasser
Copy link
Member

(welcome back @fdecampredon!)

@fdecampredon
Copy link

(thanks ! :) )

@baio
Copy link

baio commented Jan 19, 2017

Yep, there is to many <any> in my Ramda code.

@Igorbek
Copy link
Contributor

Igorbek commented Feb 21, 2017

#10247, #9949 for the reference. And see @gcnew's comment

@masaeedu
Copy link
Contributor

masaeedu commented Jul 31, 2017

Relevant paper: "Practical type inference for higher-rank types". Specifically the section on subsumption of parametrically polymorphic signatures.

Ultimately this comes down to better unification, which is what @gcnew has done some work on if you follow along in #9949. For the flip example above, the rank 1 function type <T, U>(x: T, y: U) => [T, U] can be substituted for the rank 0 function type (a: a) => (b: b) => c to obtain f: (x: a, y: b) => [a, b], because the former is more polymorphic than the latter. From there we need TypeScript's usual approach to unification to kick in and infer c = [a, b]. The tricky part is coming up with a general algorithm to do all of this.

@pelotom
Copy link

pelotom commented Nov 6, 2017

Another example:

const compose = <A, B, C>(g: (b: B) => C, f: (a: A) => B) => (a: A) => g(f(a))
const identity = <T>(x: T) => x
const lessThanTen = (x: number) => x < 10
const composed = compose(lessThanTen, identity)
//                       ^^^^^^^^^^^
// Argument of type '(x: number) => boolean' is not assignable to parameter of type '(b: {}) => boolean'.
//   Types of parameters 'x' and 'b' are incompatible.
//     Type '{}' is not assignable to type 'number'.

Can be fixed with explicit type parameters

const composed = compose<number, number, boolean>(lessThanTen, identity)

but would be really nice if this could just unify!

@Birowsky
Copy link

I would love to know if there is some movement here. Here's another usecase: https://stackoverflow.com/q/47934804/592641

@cameron-martin
Copy link

I've ran into this issue when using lodash's memoize function when the function being memoised is generic.

@steve8708
Copy link

This would also be really valuable for mixins on react components with proptypes

export class MyComponent extends MyMixin(React.Component<MyComponentProps>) {}

I love the new mixins feature since 2.2 and I was really excited about refactoring my code to create a variety of mixins for my components until I realized as of right now it won't quite work as I'd hoped without a lot of manual type declaring 😭

@kpdonn
Copy link
Contributor

kpdonn commented Jun 2, 2018

Heads up I put up my attempt at implementing this at #24626. No idea if it'll go anywhere but it works for a lot of the cases I've tried so feel free to try it out if you're interested.

@nateabele
Copy link

@kpdonn 😭 It's so beautiful.

@sibelius
Copy link

Any progress on this?

@ahejlsberg
Copy link
Member

Higher order function type inference now implemented in #30215.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Effort: Difficult Good luck. Fixed A PR has been merged for this issue Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.