Open thetutlage opened 5 years ago
More fun:
declare class Foo<T> {
public red(): this
public red(text: string): T
}
declare const foo: Foo<string> | Foo<number>
const a = foo.red('text'); // string | Foo<number>
It picks one overload from each member of the union, and both are incompatible with the call site.
I can confirm this is an issue on ts version 3.7.0-dev.20190823
.
If Foo
and Bar
are given conflicting return types for the second overload:
declare class Foo {
public red(): this
public red(text: string): string
}
declare class Bar {
public red(): this
public red(text: string): number;
}
const foo: Foo | Bar = new Foo()
const a = foo.red('text'); // string?!
a
is set to an invalid type string
when it should be string | number
.
declare class Foo {
public red(): this
public red(text: string): string
}
declare class Bar {
public red(): this
public red(text: string): number;
}
declare const foo: Foo | Bar;
//const a: string | Bar
const a = foo.red('text');
There are no generics in this example
This isn't really ideal but the overloads are in the "wrong" order and the Foo | Bar
type is subject to subtype resolution, so some strangeness is bound to happen. Fixing either of those problems independently is sufficient to make things behave as expected.
Some additional fun stuff from our gitter exploration:
Take the original example:
declare class Foo {
public red(): this
public red(text: string): string
}
declare class Bar {
public red(): this
public red(text: string): string
}
declare const foo: Foo | Bar
const a = foo.red('text') // string | Bar
Bar
fixes a
foo: Bar | Foo
makes a: string | Foo
Baz
(or more) and appending the type to foo
's union also appends it to the type of a
I think that's all I had. But it does trigger one peeve of mine: conceptually, I don't expect the ordering within a union or intersection to matter. It's been pointed out that overloads are represented as intersections, so ordering matters there. So I think this may be the smallest example that shows ordering sensitivity in both a union and an intersection.
Also @RyanCavanaugh can you elaborate on the 'right' order of overloads since I don't know of it being documented anywhere? I was thinking that it makes sense to order from more specific to more generic. My first thought was that the nullary version was most specific, but then it occurred to me that 1 arg is more 'specified' than no args, so maybe I had it backwards. If you could help my intuition there that would be great.
Its kind of sad that verified bugs are not getting fixed for years. :(
Acknowledgement
I have asked the question on Gitter and most of the users believe it's a bug and I have search for existing issues and didn't found any.
Issue
Let's start with the code. Playground link
In the above code, I expect the variable
a
to be a string. However, it isstring | Bar
.Now, things get even weird when I replace
string
withobject
in the return types ofred
method.Now suddenly,
a
is anobject
(which is correct)