-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Suggestion: Enable inferring the result type of a generic function based on the context in which it is used #3423
Comments
Case 3: Sum types (empty container sort of case) interface Optional<a> { some?: a; none?: string; }
function fromSome<a>(value: a) : Optional<a> { return { some: value }; }
function fromNone<a>(reason: string): Optional<a> { return { none: reason }; }
var value = Math.random() > 0.5 ? fromSome(123) : fromNone('Not this time'); // <-- acutual Optional<{}> expected Optional<number> |
the call to I think a better typing would not make fromNone gneric: function fromNone(reason: string) { return { none: reason }; }
var value = Math.random() > 0.5 ? fromSome(123) : fromNone('Not this time'); this way |
interface Optional<a> {
'an optional': Optional<a>; // a brand to get a nominal type: https://gist.github.com/aleksey-bykov/0ab85f0b5e83fc848f85
'depends on a': a; // an explicit yet nominal use of `a` to make it a part of the object surface: https://github.com/Microsoft/TypeScript/issues/468
}
function fromSome<a>(value: a) : Optional<a> { return <any> { some: value }; }
function fromNone<a>(reason: string) : Optional<a> { return <any> { none: reason }; }
// with type omitted
interface Optional<a> { some?: a; none?: string; }
function fromNone(reason: string) { return { none: reason; }
var noUser = fromNone('There is no user you are looking for.');
var noAccount = fromNone('There is no account either.');
noUser = noAccount; // since no type is in place the assignment is legit albeit doesn't make sense
// with type preserved
interface Optional<a> { 'uses a': a; }
function fromNone<a>(reason: string) : Optional<a> { return <any>{ none: reason; } }
var noUser = fromNone<User>('There is no user you are looking for.');
var noAccount = fromNone<Account>('There is no account either,');
noUser = noAccount; // <-- disallowed as it should be, since doesn't make sense
basically any FP languge has it, and i just wish TypeScript had some of its features too and wasn't that much of OOP and C# |
@Aleksey-Bykov I think what you are asking for is a persistent bottom type that will be inferred when there are no inference candidates, and will be subsumed by all other types. Similar to null or undefined, except that it would not widen to Your suggestion to infer from the context would work for case 1 and case 2, but not case 3 because case 3 has no contextual type, it's just a union. But actually I think @mhegazy is right that these functions should not generic. What you really want is for them to return some bottom type that will not widen. @Aleksey-Bykov I will also add that many of the cases you are mentioning suggest that you are trying to do type unification in TypeScript. The inference system in TypeScript is not unification based. Unification treats all occurrences of a type as equal citizens when it comes to inference, even if the direction of inference does not match the direction of the flow of values. By contrast, TypeScript's inference is direction sensitive. In only infers types in the direction that matches the value flow. This is why it infers from parameters, but not return types. It is the same reason TypeScript does not infer the type of a value based on how the value is used later on. |
Very well could be, although my thought was that the return types in such situation can only be inferred when there is at least one candidate whose type would be the type of the result.
I wish at this point I could say a definite yes or no, but I don't know the domain subject so well so I have to trust someone who claims to know it better.
Can't see how case 3 is different from say case 2. Anyway with new cool features of TS 1.6 (with parameters in type aliases and custom type guards) case 3 can already be taken down as irrelevant.
Indeed using a specific case of a union (a sum term) rather than a all-cases-considered container (a product) eliminates a need to for each case to be dependent on each other's case type parameters.
This is where I will be frowning. I don't have enough theoretical background to reason about whether what you just said is right or wrong. What is a type unification? Is there a link that lays it down in plain English? Similarly what is "direction sensitive" inference? Is it good or bad? I have a couple of very practical situations at hands that I wish required less typing (were inferred by the language). I hope you are not going to say it is not possible to do due to fundamental limitations that TypeScript design team has decided to embrace. |
This is a pretty concise summary of unification with an example: More to the point: Let's take case 1 that you've given (case 2 is similar). I'm going to copy it here for ease of looking: function fail<r>(message: string) : r { throw new Error(message); }
function id<a>(value: a) : a { return value; }
function withFew<a, r>(values: a[], haveFew: (values: a[]) => r, haveNone: (reason: string) => r) : r {
return values.length > 0 ? haveFew(values) : haveNone('Array is empty.');
}
var values = Math.random() > 0.5 ? ['a', 'b', 'c'] : [];
var few = withFew(values, id, fail); // <-- actual result is {}, expected is string[] My claim is that there is no useful inference that can be made from the If I understand correctly, you are suggesting that The real problem here is that an inference with no candidates yields And making |
guys, i am dying to see this on your upcoming design meeting agenda, please please please |
We generally don't bring Needs Proposal issues to the meeting. If you can figure out a way to solve this problem, we can take a closer look. Otherwise extant problems around more common patterns take higher priority in terms of us figuring out how to fix things. |
It's a very common situation in our code when we wish TypeScript could infer the result type of a function with a generic result based on the context in which that function is used. Here are a couple of use cases to support the feature request:
Case 1: Generic fail function (for being able to throw from an expression)
Case 2: Empty container constructors:
The text was updated successfully, but these errors were encountered: