Description
TypeScript Version: 3.8.0-dev.20200118
Search Terms: union of subclasses with "this" generic
Code
declare class Foo {
doThing(): Promise<this>
}
declare class Bar extends Foo {
bar: number;
}
declare class Baz extends Foo {
baz: number;
}
declare var a: Bar | Baz;
a.doThing().then(result => {
// whatever
});
Expected behavior:
a.doThing()
should have type Promise<Bar | Baz>
. result
should have type Bar | Baz
.
Actual behavior:
a.doThing()
has the type Promise<Bar> | Promise<Baz>
. The .then
call has an "This expression is not callable" error on it:
This expression is not callable.
Each member of the union type '(<TResult1 = Bar, TResult2 = never>(onfulfilled?: ((value: Bar) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<...>) | null | undefined) => Promise<...>) | (<TResult1 = Baz, TResult2 = never>(onfulfilled?: ((value: Baz) => TResult1 | PromiseLike<...>) |...' has signatures, but none of those signatures are compatible with each other.(2349)
The same behavior is observable using Array
and .map
rather than Promise
and .then
: https://www.typescriptlang.org/play/?ts=3.8.0-dev.20200118&ssl=1&ssc=1&pln=16&pc=1#code/CYUwxgNghgTiAEkoGdnwGIHtPwN4Ch4j5hMAVACwEsA7AcwAoBKALngEEYYoBPAHgAu1ZAD58AX3z5QSOImip4AIVjwQADwEgawNFhwFi8AEaw2NAK4BbYyBgBuCdPDQ5SRSoBeazdt0ZsPEJiU09za1sHJ2dZBAA3VSg2FRh4AB9lKE9HKAA6UkpaRiZcqygABwY4ZAsIAXgAXhEgiSZHIA
This behavior is what I think is appropriate here since Bar
and Baz
are both subclasses of Foo
, and therefore the doThing
function being called is the same for both types in the union. I can see that outside of this specific situation, calling a function on a union type should return the union of the possible return types. However, I think it would be better if the behavior could be different for subclasses somehow.
If not, what sort of type assertion could I use to get around this?
Related Issues: #35953 seems kinda similar, but I can't make much sense of the example given there myself.