--strictThis flag that made function this types default to void or the enclosing class (instead of any) for purposes of call-site and assignability checking, but that functionality was dropped (details). This is a follow-up suggestion for the dropped functionality, whatever the flag ends up being named.Please see https://github.com/Microsoft/TypeScript/issues/7689 for the relevant desing discussion
I just ran into a problem where the obvious solution would be that methods without an explicit this parameter default it to the this type. Searching brought me here. So the main problem is... performance? In any case, I hope a --strictThis option makes it into TS sometime (adding my 👍 ).
Maybe we can revive this thing as I believe it would bring a lot of benefit.
Regarding the "backwards compatibility" problem - is this due to class-this context for methods which in reality should stay any because they are allowed to be called from different contexts? If yes, --strictThis could only implicitly set the context to class-this for methods which have a typescript implementation and actually use this in the method body. DefinitelyTyped could adapt step by step and pure typescript code bases could use the improved type check right away.
I also hope this is added back. I just started using Typescript last week, and this is basically the only "type error" I've run into (and repeatedly for that matter) in my all Typescript codebase. I hit it frequently, as I am using an ES6 classes as a router/controller in my Express app, e.g. app.use(this.someClassFunction). When I see failing tests with an error like cannot access property someClassFunction of undefined, I just change the declaration syntax to ES7 function bind syntax class X { constructor() {} x: (req, res) => {} } and the issue is fixed, but it would be really nice to know about that at compile time instead of at runtime.
I've been impressed with Typescript in general but was very surprised to hear that it's not checked at compile time since I can't think of another typed language, where such an error is possible.
Hi, I was surprised that strict type checking for the this parameter is currently that limited and would like to see enhancements in future versions. Since the problem caused by the absence of type checking made it into the FAQ, it seems to be supising for many users.
As already mentioned in previous comments, the issues #3694 and #6018 have already proposed some changes and some of them were already implemented in the pull requests #4910 and #6739. Since there is a long history, I found it difficult to get an overview about the situation. I therefore tried to summarize it below. I hope that I did not mix up anything. At the bottom, I bring up my own thoughts about the current situation.
Proposed by #3694 and implemented by #4910. It has added a special type which represents the actual type of Within method bodies, Note that TypeScript looses the “it might be a subtype” semantic when you are outside of the class. Proposed by #3694, refined by #6018, and implemented by #6739. There were proposals for different syntaxes but I only show the implemented one. Note that this also works when a function is called as constructor. It then also specifies the type of the created object. Proposed by #3694, resumed by #6018, and implemented by #6739. It's about actually checking the argument for the Note that #3694 alternatively suggested that we could already fail when a function is assigned to an object that does not satisfy the Implemented in #6739 as replacement for However, note that it does not check call sites of such methods. Since the default type for Proposed by #6018 and implemented by #6739. It allows assigning anything to the Proposed by #6018 and implemented by #6739. In some cases, TypeScript will infer the type for If the type for
:heavy_check_mark: Polymorphic
this typethis which might be a subclass.class C {
newInstance(): this {
// Error because 'this' my be a subclass.
return new C();
}
}
class D extends C {
run() {
// Works
let x: D = this.newInstance();
}
}
this now has type this (as default).class C {
f() {
// `this` and therefore `x` have type `this`. In previous versions, they
// would have type `C`.
let x = this;
}
}
// However, for the users of the method, `this` is still considered to has
// type `any`. Even so it might cause a runtime error, the following compiles
// because `null` is assignable to `any`.
const c = new C();
c.f.call(null);
class C {
f(arg: this) {
// Error because the method might require a subtype of C.
this.f(new C()); // <-- Error
}
}
declare let c: C;
// The method might still require a subtype of C but TypeScript does not
// have this kind of semantic outside of the class.
c.f(new C()); // <-- Compiles
:heavy_check_mark: Type specifications for
this parameterfunction f(this: string) {
// A function that expects a string for the `this` parameter.
}
interface F {}
function F(this: F) {}
let f = new F(); // `f` has type `F`
:heavy_check_mark: Type checking for
this parameter on call sitesthis parameter.declare function f(this: string): any;
const obj = {f};
obj.f(); // <-- Error, `obj` is not a `string`
this parameter. Then, the second line of the code snipped would already fail. However, it was never mentioned again (and I think it would be strange).
:heavy_check_mark:
--noImplicitThis--strictThis which will be described later. The --noImplicitThis causes a compilation error when you access this but the type of this is implicitly set to any. This can happen when you access this in the function body or when you use new.function f() {
this.x; // <-- Error because type of this is unspecified.
}
function C() {}
const c = new C(); // Error because type for `this` in `C` unspecified.
this is inconsistent inside and outside of the method body, the following still compiles without errors.class C {
f() {
// Fine because `this` has type `this`.
this.f();
}
g() {}
}
const c = new C();
// Fine because `C.f` has type `(this: any) => void`.
c.f.call(null);
:heavy_check_mark: Modified assignability checks
this parameter if the this parameter is void. Usually, only undefined (or null) can be assigned to void.function f(this: void): void {}
const obj = {f};
// Passes `obj` to `void` but still compiles.
obj.f();
// Assigns `string` to `void` but still compiles.
const g: (this: string) => void = f;
:heavy_check_mark: Contextually typed
thisthis from the context. For example, when you directly assign a new function to a variable that has a declared type. The inferred type will only be used in the method body but not on call sites.declare let x: (this: string) => void;
x = function () {
// `this` has type `string`.
};
this is not specified by the target, this is contextually typed to the object where it is assigned to.const obj = {
x: 0,
f: function () {
// Ok, `this` is contextually typed as `typeof obj`.
this.x;
},
}
Proposed by a comment in #3694 and further specified by #6018 but not yet implemented. The proposal is about adding a new compiler option like The type shall be According to #7689, it has not been implemented because
:blue_book: New implicit type for
this parameter (--strictThis)--strictThis which changes the default type for this from any to either this or void.class C {
f() {
// Works since `this` is of type `this`, just as in current versions.
this.g();
}
g() {}
}
const c = new C();
// Fails because `C.f` is of type `(this: C) => void`.
// Currently, TypeScript considers the type to be `(this: any) => void`.
c.f.call(null);
this for method-style declarations and void for function-style declarations. Currently, it is always this in the method body, and any outside of the method body.// type: (this: void) => void
function f() {}
interface I {
// type: (this: void) => any
g: () => any;
// type: (this: this) => void
h();
}
const obj = {
// type: (this: typeof obj) => void
// (However, I think it should be `(this: void) => void` as I describe later)
g: function() {},
// type: (this: typeof obj) => void
h(): void {},
}
A lot of stuff have already been implemented, but we are still kind of missing safe method invocations. For me, safe method invocations actually look like one of the most important motivations behind the topic. As of today, we only get safe method invocations if we always explicitly specify the type of The problem: If we miss the type specification for either A few weeks ago, I started a project with TypeScript. At one point, I was quite confused that the following assignment works even so, it contradicts [the description of The snipped compiles due to the modified assignability checks mentioned above. If we would use I think an important objective is that the type for I think the proposal in #6018 was kind of inconsistent in this regard. If I understood the conversations correctly, whenever we use
:large_blue_diamond: Safe method invocation still missing
this.function schedule(callback: (this: void) => void) {}
class C {
data = 10;
f(this: C) {}
}
const c = new C();
schedule(c.f); // Error
schedule or f, everything compiles without errors. Additionally, one of both places might not be in our control. For example, setTimeout does not specify the type of this for the callback. Therefore, in many situations, users are not able to get safe method invocations even if they explicitly specify the type of this for all their functions and methods.
:large_blue_diamond: Use
unknown instead of voidvoid].declare let x: (this: string) => any;
declare let y: (this: void) => any;
x = y;
this: unknown instead of this: void, the special handling in the assignability checks would not be necessary, and it would not confuse new users like me. However, we can probably not revert the special handling since it would break a lot of existing code. However, if we want to change this in the long term, TypeScript could generate a warning whenever this is specified as void and recommend using unknown instead.
:large_blue_diamond: Revert part of Contextually typed
thisthis is mostly consistent between the call-site and the method body. Whenever it becomes inconsistent, it enables incorrect usage. With this objective in mind, we should revert the contextual typing when the type is not specified.const obj = {
x: 0,
f: function () {
// Currently: Ok, `this` is contextually typed as `typeof obj`
// Proposed: Error, because `this` is `void` (or `unknown`)
this.x;
},
g() {
// Continues to work sine it uses a method-style declaration
this.x;
}
};
// Currently: Ok, `obj.f` is typed as `(this: any) => void`
// Proposed (1): Ok, `obj.f` is typed as `(this: unknown) => void`
// Proposed (2): Error, `obj.f` is typed as `(this: void) => void`
obj.f.call(null);
// Currently: Ok, `obj.g` is typed as `(this: any) => void`
// Proposed: Error, `obj.g` is typed as `(this: typeof obj) => void`
obj.g.call(null);
interface I {
x: number;
f: (this: this) => void;
}
const obj2: I = {
x: 0,
f: function () {
// Ok, `this` is contextually typed as `ThisType<I['f']>`
// So, it does not change to the current behavior.
this.x;
},
};
// Copy from the proposal
interface I {
f: (n: number) => string; // this: void
g(n: number): string; // this: this
}
// Also from the proposal (but I removed a member)
let o = {
data: 12,
g: function() { // this is inferred from the contextual type
console.log(this.data);
}
}
// However, I think we should use the same rules as for interfaces and
// look at the style of the declaration.
let p = {
data: 12,
g() { // `this` has type of `p`
console.log(this.data);
},
h: function() { // `this` has type `void` (or `unknown`)
console.log(this.data); // Error
},
}
:large_blue_diamond: Avoiding performance hit
this in the facade of a type, the type becomes generic, which then causes a loss in performance. As an approximation, we could avoid the usage of this in the facade but use the most specific concrete type we know.class B {
private x = 0;
f() { // Has type `(this: B) => void` instead of `(this: this) => void`
// Within the method body, we still consider `this` to have type `this`
}
}
class C extends B {
private y = 0;
g() {} // Has type `(this: C) => void` instead of `(this: this) => void`
}
const c = new C();
// Compiles since due to the approximation, `this` has type `B` instead of `C`
c.f.call(new B());
// However, most cases like the following two are detected.
c.f.call(null); // Error
setTimeout(c.f, 0); // Error
Most helpful comment
I just ran into a problem where the obvious solution would be that methods without an explicit
thisparameter default it to thethistype. Searching brought me here. So the main problem is... performance? In any case, I hope a--strictThisoption makes it into TS sometime (adding my 👍 ).