-
Notifications
You must be signed in to change notification settings - Fork 13k
Description
Bug Report
🔎 Search Terms
The this context of type is not assignable to methods this of type
Found #23304, #28777, and #51198 which all seem somewhat different.
🕗 Version & Regression Information
Passing in TS 3.5.1, failing in TS 3.6.3 and above (including nightly 5.2.0-dev.20230526)
- This changed between versions 3.5.1 and 3.6.3
(haven't tried intermediate versions since they are not in the Playground)
(not seeing anything obvious in the 3.6 release notes
⏯ Playground Link
Playground link with relevant code
💻 Code
class Foo {
foo = 1;
public ok(): void {}
public ko(this: Foo): void {}
public overload_ok(): void;
public overload_ok(this: Foo): void;
public overload_ok(): void {}
public overload_ko(this: Foo): void;
public overload_ko(): void;
public overload_ko(): void {}
}
class Bar {
public ok(): void {}
public ko(): void {}
public overload_ko(): void {}
public overload_ok(): void {}
}
function f(x: Foo | Bar): void {
x.ok();
x.ko(); // error
// The 'this' context of type 'Foo | Bar' is not assignable to method's 'this' of type 'Foo'.
// Property 'foo' is missing in type 'Bar' but required in type 'Foo'.
x.overload_ok();
x.overload_ko(); // error
// The 'this' context of type 'Foo | Bar' is not assignable to method's 'this' of type 'Foo'.
// Type 'Bar' is not assignable to type 'Foo'.
}
function g(x: Foo | Bar): void {
if (x instanceof Foo) {
x.ok();
x.ko();
x.overload_ok();
x.overload_ko();
} else {
x.ok();
x.ko();
x.overload_ok();
x.overload_ko();
}
}
🙁 Actual behavior
The calls to x.ko()
and x.overload_ko()
throw type errors in f
.
🙂 Expected behavior
So, the signatures of ko
and ok
are slightly different (in Foo
), but since ko
is already a method on the class Foo
, it should only be called when this
is an instance of Foo
anyway, so it feels like the "this constraint" shouldn't do anything.
The overload_*
cases feel even weirder. In overload_ok
, the first overload is definitely a catch all, so it is the only one used (as far as I understand), and since it is the same as the ok
signature, everything works.
But in overload_ko
, the first overload is the same as the ko
signature and fails for the same reason, but the second overload (which is the same as the ok
signature and thus should work) is not even tried (my IDE also says it is never called). So, it seems that TS treats the first overload (of overload_ko
) as a catch all case and stops looking further… except it is actually not behaving the same way. TS even tries to be helpful adding:
public overload_ko(): void {}
~~~~~~~~~~~
The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.
Since the implementation signature is exactly the same as the second overload, this shows that TS totally ignored the second overload, yet doesn't treat the first one as being fully a catch all case, since stuff breaks with the first one that would work with the second.
g
is a workaround where forcing the narrowing and breaking the union in advance resolves the problem. However, having to WET my code is not really doable in practice; and having both the "then" and "else" branchs with the exact same code is such an anti-pattern that it makes my programmer lizard brain scream 🙈
Of course, there may be something I do not really understand in what the "this constraint" actually does. This looks very weird from where I stand now.