Skip to content

Return types of intersection of functions are incomplete and depend on order of declaration - an algorithm to fix it #57089

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

Closed
6 tasks done
craigphicks opened this issue Jan 18, 2024 · 4 comments

Comments

@craigphicks
Copy link

craigphicks commented Jan 18, 2024

πŸ” Search Terms

intersection of functions, return type, incomplete, declaration order dependent

#57002

βœ… Viability Checklist

⭐ Suggestion

The return type of an intersection of functions should be complete and independent of the order of declaration of the functions.

An algorithm is proposed to achieve that.

πŸ“ƒ Motivating Example

interface A0 {
    foo(): string;
}

interface B0 {
    foo(): number;
}

declare const ab0: A0 & B0;
declare const ba0: A0 & B0;

const rab = ab0.foo(); // actual string, expecting string | number
const rba = ba0.foo(); // actual number, expecting string | number

interface A1 {
    bar(x:1): 1;
    bar(x:2): 2;
}
interface B1 {
    bar(x:2): "2";
    bar(x:3): "3";
}

declare const ab1: A1 & B1;

const rab11 = ab1.bar(1); // actual 1, expecting 1
const rab12 = ab1.bar(2); // actual 2, expecting 2 | "2"
const rab13 = ab1.bar(3); // actual 3, expecting 3

declare const ba1: B1 & A1;

const rba11 = ba1.bar(1); // actual 1, expecting 1
const rba12 = ba1.bar(2); // actual "2:, expecting 2 | "2"
const rba13 = ba1.bar(3); // actual 3, expecting 3

The proposed algorithm is

  • Let g = g[0], g[1], ... be intersection of functions.
  • Let args be the arguments.
  • returnType = never // initialize
  • for g[i] in g
    • if args extends Parameters<g[i]> then
      • returnType = returnType & ReturnType<g[i]>
  • return returnType

The current algorithm is just to route the call to chooseOverload and treat bar exactly as though it were an ordered overload function. Therefore bar can match at most one of its intersection members, resulting in an incomplete and declaration order dependent return type.

πŸ’» Use Cases

Would resolve issue #57002.

What workarounds are you using in the meantime?

There is no easy workaround now.

@MartinJohns
Copy link
Contributor

You are contradicting yourself. In your motivating examples you use unions for the return types, in your "proposed algorithm" you use an intersection for the return types.

@craigphicks
Copy link
Author

craigphicks commented Jan 18, 2024

@MartinJohns - I am well aware of that (notice that the issue is closed). Consider this intersection:

interface A2 {
    foo(): {a: string};
}
interface B2 {
    foo(): {b: string};
}
type C2 = A2 & B2;

Then an & is desired, whereas in A1&B1, a | is desired.

Talking about the & in A&B, what is it? Fundamentally, & occurs naturally (think wild caught) in flow processing when two functions f1 and f2 are in superposition - the domain of inputs that satisfies both function is the intersection of Parameters<typeof f1> & Parameters<typeof f2>. There are any number of argument subtype implementations that satisfy that constraint. See #57087 (not yet opened).

In #57087, the user is always required to define a (usually overloaded) function type to pass as the argument to be checked by the parameter intersection type. The whole argument is checked against the &ed parameter.

In contrast, here in #57089 we are querying locations of a manually defined (think factory farmed) intersection type and getting back an arbitrary, narrow type value that satisfies that type. What users seems to need here is to get, at each query point, the union of all intersection-valid argument types at that query point.

Another issue (not this one) is that using a manually defined intersection to define a type - often used as a quick way to define a composite type - is not a good way to define a composite type. It would be better to define a composition tool(s) to create composite types from other types. Type composition need not be equal to type intersection, the latter of which has a specific meaning in CFA.

@fatcerberus
Copy link

notice that the issue is closed

It’s not (you appear to have reopened it prior to Martin’s reply).

@craigphicks
Copy link
Author

My bad.

Re-opened as #57095.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants