Skip to content

Parameter type narrowing should consider function overload signatures #14515

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

Open
axefrog opened this issue Mar 7, 2017 · 4 comments
Open

Parameter type narrowing should consider function overload signatures #14515

axefrog opened this issue Mar 7, 2017 · 4 comments
Labels
Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript

Comments

@axefrog
Copy link

axefrog commented Mar 7, 2017

TypeScript Version: 2.2.1

Code

interface A { x: number; }
interface B { y: number; }

function foo(b: B): void;
function foo(a: A, b: B): void;
function foo(a: A|B, b?: B): void {
  if(b !== void 0) {
    console.log(a.y); // Error: Property 'y' does not exist on type 'A | B'
  }
}

The third (unified) function signature is hidden from the call site, and therefore any arguments should be able to be narrowed based on inspection of the provided arguments. In the case above, the second argument is only defined if the first argument is of type A, and so I would expect narrowing of the argument type to occur accordingly. One could make the argument that in the compiled output, it's possible for JavaScript to still pass subsequent arguments, but then why narrow types at all, seeing as JavaScript can always violate every type constraint that TypeScript makes inferences from?

@RyanCavanaugh RyanCavanaugh added Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript labels Mar 7, 2017
@RyanCavanaugh
Copy link
Member

This is easy enough to reason about for two overloads but quickly gets out of hand for more complex scenarios. It's going to be very complex to produce a single data structure which succinctly defines the relationship between these parameters:

function fn(x: string, y: number): void;
function fn(x: string, y: string: void;
function fn(x: number, y: number, z: string): void;
function fn(x: number | string, y: number | string, z?: string) {
 // Quick, what checks produce what type guards?
}

We also don't have any mechanism for "if x is T then y is U" inferences.

@axefrog
Copy link
Author

axefrog commented Mar 7, 2017

Couldn't you use a simple graph for this? It'd have an entry node for each argument, with convergence where commonalities appear among overloads. You could use something like a suffix tree that wraps back in on itself. Radix trees also seem relevant, but are constrained by ordinality. Still, might be good inspiration for a versatile solution.

@AnyhowStep
Copy link
Contributor

@axefrog

This is two years late but,
#33704 (comment)

In some cases, you can get the kind of narrowing you want.

@boris-kolar
Copy link

boris-kolar commented Oct 1, 2019

Interestingly, a somewhat isomorphic example to #33704 already works as expected, so it might be possible to do something about this issue:

type __foo_params =
    | {a?: never, b?: never}
    | {a: string, b: string}

function foo(__params: __foo_params): void {
    if (!__params.a) return
    console.log(__params.a.length + __params.b.length) // no error
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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

No branches or pull requests

4 participants