I cannot make sense of how TS is inferring this generic this: playground
class Base<T> {
baseMethod<This extends Base<T>>(this: This): 123 extends This ? { x: 1, y: 1 } : { x: 2, z: 3 } {
return {} as any;
}
}
class Derived<T> extends Base<T> {
derivedMethod() {
const res = this.baseMethod();
res.x; // 1 | 2
res.z; // ERROR: property does not exist
}
}
How is it possible that it's returning the union of both branches of the type conditional? I'd expect to return the second one (or even the first one, if This was somehow inferred as any or unknown. But both?)
Note 1: If I remove <This extends Base<T>>(this: This) and simply use 123 extends this instead (I guess they are equivalent and I don't need This), the result is the same, it returns the union.
Note 2: As @caTS mentioned, if you explicitly write derivedMethod(this: Derived<T>) then it works. But why would TS need that?
The error seems to be the result of the fact that typescript defers to compute types derived from
thisinside methods because those types might be altered in further derived classes. This prevents it in the current version to perform even legitimate evaluation, as expected in @tokland's question.If we consider the example
the deferment is fully justified, since
Bdoesn't satisfy the condition{x: number} extends B, but a derived classdoes. (playground)
This is similar to the case in this typescript issue.
The problem is that in some cases there's no need to defer computing with
thistype; that is the case in the code from OP's question, or in this "extreme" example(playground)
I suppose we may expect from (ask for?) future releases to improve at this point.
OP's question may be reduced (I'll show in the end the steps I took) to the following, more familiar version of
B:It may be corrected as per @caTS's suggestion by setting the type of
thisin the method, and (surprisingly for me) this case also requires replacingthisas a type - which still remains untouched - withtypeof this.(playground)
The steps I took to simplify the code from the question:
T: https://tsplay.dev/WzPpLNThischanges somewhat the semantics - https://tsplay.dev/mx8bBm, but it seems the source of the error is the same.