You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Infer overloads instead of union returns from flow analysis #17747
constflip=(x: number)=>x.toString()constflop=(x: string)=>x.length// What is the return type of flipFlop?constflipFlop=(x: string|number)=>isNumber(x) ? flip(x) : flop(x)
Today flipFlop is inferred as having a return type of string | number. Intuitively though, flipFlop is an overloaded function of type (x: number) => string & (x: string) => number.
Given that TypeScript already performs flow analysis in order to discover the union return type string | number, it seems like some useful information is being discarded. Specifically, when performing the analysis that yields a return type of number | string, it seems a shame to throw away the associated information that x is narrowed to number in the exit block where the function's return type is string, and vice versa.
If we instead utilize this information, it becomes possible to infer the much more accurate type (x: number) => string & (x: string) => number.
In general, it might be nice if union parameters, overloaded functions, and control flow could be integrated more smoothly.
A function type with a union parameter (x: number | string) => Date is basically equivalent to the overloaded function (x: number) => Date & (x: string) => Date. Similarly, a branched function with various return points (such as flipFlop) is overloaded with respect to the return type.
If the CFA treats each type in the union type of a parameter as a separate entry point, and solves the control flow for each indepenently, it should be possible to discover multiple independent mappings of input types to output type (i.e. overloads).
For functions with multiple parameters of union types, the overloads inferred must be a cartesian product of the unions. This can get expensive quickly, but some analysis of the function body to predict which types are actually discriminated against should allow "inert" unions that do not participate in control flow to be grouped together and treated as a single overload/entrypoint.
After adding conditional types and trying out some related patterns with other features, this doesn't seem like a good fit. Once you start pushing the CFA through to the return type, you can quickly end up with a return type expression that looks like an unholy restating of the function implementation, and in general function implementations should be opaque by default - the caller today might return a string when given a number and vice versa, but the maintainable-by-default version is to not expose the internal logic of your function unless that's what you actually intend to do.
Activity
RyanCavanaugh commentedon Sep 18, 2019
After adding conditional types and trying out some related patterns with other features, this doesn't seem like a good fit. Once you start pushing the CFA through to the return type, you can quickly end up with a return type expression that looks like an unholy restating of the function implementation, and in general function implementations should be opaque by default - the caller today might return a
stringwhen given anumberand vice versa, but the maintainable-by-default version is to not expose the internal logic of your function unless that's what you actually intend to do.