New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(eslint-plugin): add no-implicit-this-methods rule #1279
Conversation
Thanks for the PR, @Retsam! typescript-eslint is a 100% community driven project, and we are incredibly grateful that you are contributing to that community. The core maintainers work on this in their personal time, so please understand that it may not be possible for them to review your work immediately. Thanks again! 🙏 Please, if you or your company is finding typescript-eslint valuable, help us sustain the project by sponsoring it transparently on https://opencollective.com/typescript-eslint. As a thank you, your profile/company logo will be added to our main README which receives thousands of unique visitors per day. |
Adding I feel like maybe such rules should start as suggestion issues, to gather feedback/upvotes from the community before implementation. There are a certain set of rules that are perhaps too specific for general usage, and should maybe be included in separate packages. |
@glen-84 It may be worth clarifying that it's not every method: it's only methods that use I'd defend this rule's inclusion in the main typescript-eslint package, as it closes one of the few sources of runtime errors I've found in strict Typescript - but I'm fine with moving this to a suggestion issue and gathering feedback in the meantime. (I don't feel the time spent implementing it is wasted, if it doesn't get used here I'll likely move the code elsewhere) |
That's most methods in the codebases that I've worked on. Doesn't the I prefer the idea of flagging unbound methods and fixing only those cases, as opposed to requiring all methods to be declared this way, even if they're never called without the correct Anyway, this is just my opinion, other developers may also want this rule. |
Codecov Report
@@ Coverage Diff @@
## master #1279 +/- ##
==========================================
- Coverage 94.06% 94.03% -0.03%
==========================================
Files 131 132 +1
Lines 5745 5819 +74
Branches 1617 1645 +28
==========================================
+ Hits 5404 5472 +68
- Misses 183 186 +3
- Partials 158 161 +3
|
This does do fairly similar things to Would it change opinions at all if this rule had an auto-fixer? That would make it much easier for existing codebases to adopt this change. |
Not for me personally. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't realise that typescript has such good support for this: this
. (a few more cool examples)
const explicit = new Example().explicit;
// typeof explicit === (this: Example) => string
const boundExplicit = explicit.bind(new Example());
// typeof boundExplicit === () => string
However, there is one case that this feature does not catch: passing a method with a this: this
to a function will not trigger any errors.
This is by far the most common cause of bugs due to unbound this
. eg:
- Passing unbound methods as a callback to things like
window.addEventListener
- Passing unbound methods down the react tree as JSX props.
function acceptsStringFn(fn: () => string): string {
return fn();
}
// runtime errors for both, but no compile-time errors
acceptsStringFn(new Example().explicit);
const x = <div onClick={new Example().explicit} />
On the other hand, the unbound-method
rule catches this.
Which leads me to the question - why would I use this rule instead of unbound-method
?
I've always been of the opinion that you should use arrow functions for class methods using this
, so that the this
is implicitly bound correctly always.
Out of curiosity - may I ask why you use methods instead of arrow functions for this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(RC for now to remove from queue whilst questions are answered)
I think your questions have already been answered (?).
|
Kind of does. The latter one is still open - you mention you had "relatively few methods where this rule applied", what were those cases and why couldn't they be arrow functions instead? The former, I get not wanting to use type information, but I don't think this is an amazing alternative, due to the "pass as argument" hole I mentioned earlier. Personally, I'd rather false positives which I can assess and silence, rather than false negatives which are missed and cause production bugs. I wonder if a better rule would be - "always use an arrow function if referencing |
Yeah, after playing with this rule a bit more, I'm not satisfied with it myself. I also discovered the "pass as argument" hole, and found a few annoying edge cases. (e.g. it's a little non-trivial to annotate the type of Our codebase has a few places where we've specifically avoided arrow functions for memory reasons - places where we create thousands of instances and the memory pressure of each instance having its own copies of every method was noticeable, compared to a single shared instance on the prototype - but that's probably an anomaly. I'm still not a huge fan of Thanks for the discussion and the feedback! |
Note that if you define function acceptsStringFn(fn: (this: void) => string): string {
return fn();
}
// runtime *and* compile-time errors
acceptsStringFn(new Example().explicit); But I think it's a very big ask that:
Ideally TypeScript would infer |
That's interesting, I haven't ever used the I wouldn't completely be opposed to a rule which enforces that every single function includes a I'm not sure if people would use it (because it's a weird thing to require), but it seems like it could be a pretty useful rule if it had a fixer for all cases. |
Ah, I didn't realize that either. I think you've got an error in the example, though. I think it's supposed to be function acceptsStringFn(fn: (this: void) => string): string not function acceptsStringFn(this: void, fn: () => string): string (Which means not every function needs I guess in theory, a slightly more useful version of this linter rule would also check that higher order functions specify Though, in my experience the biggest mistake in terms of (Also, sorry, I guess I could have saved you some time if I had linked #microsoft/TypeScript#35451 here) |
I was also curious about this. If there is no |
Copying description from the readme here for convenience. This rule closes a class of potential runtime errors when dealing with
this
keywords.No implicit
this
type for methodsThis rule requires that all class and object methods that use the
this
keyword must explicitly specify thethis
type, so that the typescript compiler will ensure that the function is always called with the rightthis
. For example:The compiler has a built-in
--noImplicitThis
option, but it allowsthis
to be implicit in classes and object methods.