Skip to content
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

Explicit this: this annotation necessary to catch unbound class method errors with --no-implicit-this #35451

Closed
Retsam opened this issue Dec 2, 2019 · 2 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@Retsam
Copy link

Retsam commented Dec 2, 2019

TypeScript Version: 3.7.2

Search Terms: this binding, --no-implicit-this, methods, this: this

Expected behavior: The unbound this error is raised in both cases.

Actual behavior: The compiler only catches the error in the case where the method was annotated as this: this.

Related Issues: Found many this issues, couldn't find any related to the default inferred value in the absence of a this annotation on class methods. Sorry if I've just missed it.

Code

class Example1 {
    value = "value";
    method() {
        return this.value.toUpperCase();
    }
}
// Runtime-error, no compiler error
new Example1().method.call(null);

class Example2 {
    value = "value";
    method(this: this) {
        return this.value.toUpperCase();
    }
}
// Compiler error, as expected
new Example2().method.call(null);
Output
"use strict";
class Example1 {
    constructor() {
        this.value = "value";
    }
    method() {
        return this.value.toUpperCase();
    }
}
// Runtime-error, no compiler error
new Example1().method.call(null);
class Example2 {
    constructor() {
        this.value = "value";
    }
    method() {
        return this.value.toUpperCase();
    }
}
// Compiler error, as expected
new Example2().method.call(null);
Compiler Options
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "useDefineForClassFields": false,
    "alwaysStrict": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "downlevelIteration": false,
    "noEmitHelpers": false,
    "noLib": false,
    "noStrictGenericChecks": false,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "esModuleInterop": true,
    "preserveConstEnums": false,
    "removeComments": false,
    "skipLibCheck": false,
    "checkJs": false,
    "allowJs": false,
    "declaration": true,
    "experimentalDecorators": false,
    "emitDecoratorMetadata": false,
    "target": "ES2017",
    "module": "ESNext"
  }
}

Playground Link: Provided


Given this behavior, I've submitted a PR to typescript-eslint adding a linter rule that requires these annotations on all (non-arrow function) class methods that use this.

However, I was wondering if there was a way this could be resolved on the Typescript compiler's side. The two code examples above seem semantically identical to me, and it's strange that they type-check differently. Is there a reason why it'd be harmful for this: this to be inferred from class methods that lack an explicit type annotation?

Naturally, it would have to be behind a flag, but this is one of the few areas where I've hit runtime errors that the TS compiler hasn't caught. It's led to our codebase being somewhat paranoid about attaching methods to the prototype, preferring to define almost all methods as arrow function instance properties, which is a bit unfortunate from a memory perspective.

Or perhaps there's some downside to this: this that makes it unsuitable as a general practice, and I shouldn't have a lint rule enforcing that at all! Any insight on the current behavior would be appreciated.

Thanks!

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Dec 12, 2019
@RyanCavanaugh
Copy link
Member

Adding a this parameter effectively makes the method generic; making all methods in classes generic by default has horrifying performance implications (think 20%+ increases in compilation times in class-heavy codebases). See comments in #6739

@Retsam
Copy link
Author

Retsam commented Dec 13, 2019

Ah, thanks, I think that was the piece of the puzzle I was missing. It makes sense that the user can opt-in to some better type checking at the expense of somewhat slower compilation.

I'll have to figure out whether the performance downside, plus some missed edge cases, plus the syntax inconvenience makes the lint rule worth having...

Anyways, thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

2 participants