microsoft / microsoft/TypeScript

3.5 regression: return type forced more restrictive than needed

Open
#31,316 7 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Domain: check: Control Flow Domain: Indexed Access Types Needs Proposal Suggestion
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
2d 4h
Merged PRs (30d)
132

Description

TypeScript Version: 3.5.0-dev.20190508

Search Terms: restrictive return type

Code

class Box<A> {
    constructor(public val: A) {}
}

interface ContainerFor<A> {
    array: Array<A>;
    box: Box<A>;
}

function single<T extends keyof ContainerFor<number>>(t: T, x: number): ContainerFor<number>[T] {
    switch (t) {
        case "array":
            return new Array<number>(x);
        case "box":
            return new Box<number>(x);
    }
    return new Box<number>(0); // this should not be needed
}

function test() {
    let t1 = single("array", 42);
    let t2 = single("box", 42);
    //let t3 = single("boo", 42);
}

Expected behavior:
This code worked with 3.4, but had an unsoundness bug in that single was allowed to return the wrong type (swap array and box). It was clear that switching over t did not increase knowledge about the expected output type and I was forced to have a fallback return which is of course unreachable. But the code itself worked.

The test function also shows things work as expected. The types for t1 and t2 are inferred correctly and asking for an unknown thing ("boo") is reported as an error.

I would expect this to still work in 3.5, perhaps fixing one of the issues I'm having in the internals of single.

Actual behavior:
Typescript 3.5 no longer accepts this code. returning new Array or new Box are flagged as wrong return types. The newly expected return type is now mandated to be the intersection type: number[] & Box<number>, which is too restrictive as the correct output type can be statically determined from T in the input. The fact that test correctly infers the output type is proof of this.

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

Reproduce the generic function example with TypeScript 3.5.0-dev.20190508 and compare its behavior with TypeScript 3.4, focusing on how the switch over T determines the indexed return type. Done means the array and Box branches are accepted without the unreachable fallback while invalid keys remain errors.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.