-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
question: how can i create and return a generic function in TypeScript? #9949
Comments
When TS sees What you would need is for TS to lift the type parameters up a level rather than just defaulting them. That is, if any type parameters needed to be defaulted when inferring the type of an expression (eg I'm not sure what the broad implications of that would be, though. |
I took a stab at this and made a hacky implementation. It's by no means complete, but it can be used to play around with. Current progress: function flip<A, B, R>(f: (a: A, b: B) => R): (b: B, a: A) => R {
return (b, a) => f(a, b);
}
function id<T>(x: T): T {
return x;
}
function fconst<X, Y>(x: X, y: Y): X {
return x;
}
function addStr(x: number, y: string) {
return x + y;
}
function tagged<T extends string, Q>(tag: T, value: Q): { tag: T, value: Q } {
return { tag, value };
}
// it was working before
const f1 = flip(addStr); // (b: string, a: number) => string
const v1 = f1("hello", 3);
const v2 = id(id)(3); // `3`
// working now
const f2 = flip(id); // <T>(b: {}, a: T): T
const f3 = flip(fconst); // <Y, X>(b: Y, a: X) => X
const v3 = f3(1, "qw") // `"qw"`
const v4 = f3([], {}) // `{}`
const f4 = flip(tagged); // <Q, T extends string>(b: Q, a: T) => { tag: T, value: Q }
const v5 = f4(5, "hello"); // { tag: "hello", value: number }
const v6 = f4(5, 5); // Error as expected
declare function compose<A, B, C>(f: (b: B) => C, g: (a: A) => B): (a: A) => C;
declare const f: <T>(x:number) => T;
declare const g: (x:boolean) => number;
const f5 = compose(f, g) // OUCH! this gets type `<T>(a: boolean) => T`
declare const g_2: <T>(x: T) => boolean;
declare const f_2: (x: boolean) => number;
const f6 = compose(f_2, g_2) // <T> (a: T) => number
const f7 = compose(id, x => String(x)) // (a: {}) => string
// Was working, now not:
declare function h<R>(f: (x: number) => R): R;
var z: number = h(id); // number ~ T, R ~ T
// backpropagation is now broken
const arr = [1, 2, 3].map(id); // T[] Snap :( See https://github.com/gcnew/TypeScript/tree/polyFuncUnification Edit: updated branch link and the examples |
@mhegazy What should the proposal look like? |
mainly how it would be implemented. implications on different parts of the system, e.g. assignablity ( |
Man, this is already an awesome improvement as-is, hope this could make it in. @gcnew: what would you be expecting for That |
@tycho01 Thank you! Yes, you are right about PS: I've pushed a fix for what you are observing on |
Hah, |
I just pushed a greatly improved unification version. It works 80% of the time (with some corner cases left out). I'm still going through the failed test cases, but I think other people can give it a try as well. |
Any ideas when/if this will land? Trying to determine if I should redesign my code (which assumed unbound types would be carried forward) or just wait for this to land... |
I wouldn't depend on it. Currently it's not on the roadmap and the core TS team doesn't look very interested in this feature. To support it, the compiler will need some fairly substantial changes, or better said a rearchitecture of the inferencer. I've layed out the major hurdles in this comment. |
Just wanted to up this feature, because it requires very odd additions when working with higher-order functions (like in typed-typings/npm-ramda#175). |
looks like a duplicate of #9366 |
Fixed with #16072. |
Fixed is a bit of an overstatement, but #16072 helps. |
@gcnew I've yet to really test; would you have examples still failing? |
All polymorphic higher order uses still continue to fail as noted in the PR. For example, all declare function flip<A, B, R>(f: (a: A, b: B) => R): (b: B, a: A) => R;
declare function fconst<X, Y>(x: X, y: Y): X;
const f1 = flip(fconst); // (b: {}, a: {}) => {}
const f2: (b: string, a: number) => number = flip(fconst); // OK, now it's working
const f3: (b: string, a: number) => string = flip(fconst); // Error correctly caught
// assignment works but all for the wrong reasons
// flip's A, B and R are inferred as `any` :/
const f4: <A, B>(a: A, b: B) => B = flip(fconst);
// uncaught error and implicit any not reported :/
const f5: <A, B>(a: A, b: B) => A = flip(fconst); #16072 makes |
I made a quick implementation of my suggestion from #16114 (comment). This work (attempt4) builds on #16104 and shows promising results, however there are still some important issues left. I do believe that this approach is working and the issues will be resolved with more effort put into them. Issues:
The test cases that I've used are here (gist). With a few exceptions, most of them yield what's expected. PS: The code is not well written. My primary aim was to verify that the proposed logic could be made to work. If all goes well, I'll refactor it into something maintainable. PS2: This version of the compiler cannot build itself. However, it works fine if built externally. I've modified the jakefile so that |
See https://github.com/reactjs/redux/blob/v3.7.0/index.d.ts See reduxjs/redux#1936 (comment) See Function composition challenge for type system microsoft/TypeScript#10247 See microsoft/TypeScript#9949
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed. |
@mhegazy problem still there, please reopen |
It is a duplicate/same underlying issue as #9366. |
Fixed in #30215. |
The text was updated successfully, but these errors were encountered: