microsoft / microsoft/TypeScript

Proposal: better type narrowing in overloaded functions through overload set pruning

Open
#22,609 16 comments 44 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

In Discussion Suggestion
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
2d 4h
Merged PRs (30d)
132

Description

TypeScript Version: 2.7.2

Search Terms: type inference, type narrowing, overloads

Code

function bar(selector: "A", val: number): number;
function bar(selector: "B", val: string): string;
function bar(selector: "A" | "B", val: number | string): number | string
{
    if (selector === "A")
    {
        const the_selector = selector;  // "A"
        const the_val = val;            // number | string but should be just number
        return "foo";                   // should give error: when selector is "A" should return a number
    }
    else
    {
        const the_selector = selector;  // "B"
        const the_val = val;            // number | string but should be just string
        return 42;                      // should give error: when selector is "B" should return a string
    }
}

Issue

The snippet above shows a limitation of the current type inference implementation in TypeScript.
As can be clearly seen from the overloaded declarations, when selector === "A" we have val: number and we must return a number, while when selector === "B" we have val: string and we must return a string. But the type-checker, although able to understand that the_selector can only be "A" in the if block and only "B" in the else block, still types the_val as number | string in both blocks and allows us to return a string when selector === "A" and a number when selector === "B".

Possible Solution

In the implementation of an overloaded function, when the type-checker decides to narrow the type of a parameter (as often happens in if-else blocks), it should also remove the overloads that don't match the narrowed parameter type from the overload set, and use the pruned overload set to narrow the types of the other parameters.

Playground Link: https://www.typescriptlang.org/play/#src=%0Afunction%20bar(selector%3A%20%22A%22%2C%20val%3A%20number)%3A%20number%3B%0Afunction%20bar(selector%3A%20%22B%22%2C%20val%3A%20string)%3A%20string%3B%0Afunction%20bar(selector%3A%20%22A%22%20%7C%20%22B%22%2C%20val%3A%20number%20%7C%20string)%3A%20number%20%7C%20string%0A%7B%0A%20%20%20%20if%20(selector%20%3D%3D%3D%20%22A%22)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20const%20the_selector%20%3D%20selector%3B%0A%20%20%20%20%20%20%20%20const%20the_val%20%3D%20val%3B%0A%20%20%20%20%20%20%20%20return%20%22foo%22%3B%0A%20%20%20%20%7D%0A%20%20%20%20else%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20const%20the_selector%20%3D%20selector%3B%0A%20%20%20%20%20%20%20%20const%20the_val%20%3D%20val%3B%0A%20%20%20%20%20%20%20%20return%2042%3B%0A%20%20%20%20%7D%0A%7D

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the overloaded function example in the linked TypeScript Playground and inspect the type-checker behavior for narrowed parameters. Compare the inferred types and accepted return statements in each branch. Done means the overload set is narrowed consistently so the related parameter and return type are checked against the surviving overloads.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.