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:
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 {}.


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.