Skip to content

Confusing error when returning generic in terms of this from method called on union of subclasses #36307

Closed
@eritbh

Description

@eritbh

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
});

Playground Link

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.

Metadata

Metadata

Assignees

Labels

Fix AvailableA PR has been opened for this issueNeeds InvestigationThis issue needs a team member to investigate its status.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions