Typescript: Typescript 3.4 breaks some type inference with method overloads. (unknown)

Created on 11 Feb 2020  路  5Comments  路  Source: microsoft/TypeScript

TypeScript Version: 3.4 and up


Search Terms:
unknown, 3.4, type inference fails, method overload

Code

type Circle = {
    type: "CIRCLE";
    radius: number;
};

type Square = {
    type: "SQUARE";
    sideLength: number;
};

type Shape = Circle | Square;


class Evt<T> {

    public attach<U>(
        matcher: (data: T)=> [U] |聽undefined,
        callback: (transformedData: U)=> void
    ): Promise<U>;
    public attach(
        matcher: (data: T) => boolean,
        callback: (data: T) => void
    ): Promise<T>;
    public attach(...args: any[]){
        return null as any;
    }

}

const evtShape = new Evt<Shape>();

//OK
const prRadius = evtShape.attach(
    shape => shape.type === "CIRCLE" ? [shape.radius] : undefined,
    radius => { }
);


const prShape = evtShape.attach(
    shape => true,
    shape => { } //<====== In typescript 3.4 and up shape is unknown 
);

Expected behavior:

In the last line, shape should be of type Shape. In typescript 3.4 and up it is of type unknown.

Actual behavior:

Playground Link

Question

All 5 comments

Slightly shorter

declare class Stream<T> {
    compactAndThen<U>(matcher: (data: T) => [U] | undefined, callback: (transformedData: U) => void): Promise<U>;
    compactAndThen(matcher: (data: T) => boolean, callback: (data: T) => void): Promise<T>;
}

const thing = new Stream<string>();

//OK
const prRadius = thing.compactAndThen(
    str => str.length > 0 ? [str.toLowerCase()] : undefined,
    str => { }
);


const prShape = thing.compactAndThen(
    foo => true,
    bar => { } //<====== In typescript 3.4+, 'bar' is unknown 
);

Looks like bar gets unknown due to no inferences from the first overload. Not sure if there's a way to fix this. @ahejlsberg?

After further investigation, I noticed that event in typescript prior to 3.4 altho the type Shape is inferred it can only be used as a {}.

Screenshot 2020-02-13 at 08 54 42

Screenshot 2020-02-13 at 08 50 43

Thanks to the typescript's team, you guys do amazing work <3

Hello,
Just a quick update to say that this limitation is a big deal for the library I am working on: EVT.
The overload of the "attach" method had to be split into two groups "attach" and "$attach" and peoples get confused about why and when they need to add the "$".
I know this is really pushing the already amazing type inference system but it would be great for EVT to be able to merge all the overload together.

Regards

The two overloads are just too close for us to distinguish between them. Best alternative I can come up with is a single signature that uses a conditional type:

declare class Stream<T> {
    compactAndThen<U extends [any] | boolean>(matcher: (data: T) => U | undefined, callback: (data: U extends [infer V] ? V : T) => void): Promise<U extends [infer V] ? V : T>;
}

This seems to deliver the desired results.

@ahejlsberg Thank you very much for taking the time to come out with a workaround, it is very clever.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kimamula picture kimamula  路  147Comments

sandersn picture sandersn  路  265Comments

jonathandturner picture jonathandturner  路  147Comments

tenry92 picture tenry92  路  146Comments

Gaelan picture Gaelan  路  231Comments