unbound-method
Enforce unbound methods are called with their expected scope.
Extending "plugin:@typescript-eslint/recommended-type-checked"
in an ESLint configuration enables this rule.
This rule requires type information to run.
Class method functions don't preserve the class scope when passed as standalone variables ("unbound").
If your function does not access this
, you can annotate it with this: void
, or consider using an arrow function instead.
Otherwise, passing class methods around as values can remove type safety by failing to capture this
.
This rule reports when a class method is referenced in an unbound manner.
If you're working with jest
, you can use eslint-plugin-jest
's version of this rule to lint your test files, which knows when it's ok to pass an unbound method to expect
calls.
module.exports = {
"rules": {
"@typescript-eslint/unbound-method": "error"
}
};
Try this rule in the playground ↗
Examples
- ❌ Incorrect
- ✅ Correct
class MyClass {
public log(): void {
console.log(this);
}
}
const instance = new MyClass();
// This logs the global scope (`window`/`global`), not the class instance
const myLog = instance.log;
myLog();
// This log might later be called with an incorrect scope
const { log } = instance;
// arith.double may refer to `this` internally
const arith = {
double(x: number): number {
return x * 2;
},
};
const { double } = arith;
Open in Playgroundclass MyClass {
public logUnbound(): void {
console.log(this);
}
public logBound = () => console.log(this);
}
const instance = new MyClass();
// logBound will always be bound with the correct scope
const { logBound } = instance;
logBound();
// .bind and lambdas will also add a correct scope
const dotBindLog = instance.logUnbound.bind(instance);
const innerLog = () => instance.logUnbound();
// arith.double explicitly declares that it does not refer to `this` internally
const arith = {
double(this: void, x: number): number {
return x * 2;
},
};
const { double } = arith;
Open in PlaygroundOptions
This rule accepts the following options:
type Options = [
{
/** Whether to skip checking whether `static` methods are correctly bound. */
ignoreStatic?: boolean;
},
];
const defaultOptions: Options = [{ ignoreStatic: false }];
ignoreStatic
Examples of correct code for this rule with { ignoreStatic: true }
:
class OtherClass {
static log() {
console.log(OtherClass);
}
}
// With `ignoreStatic`, statics are assumed to not rely on a particular scope
const { log } = OtherClass;
log();
Open in PlaygroundWhen Not To Use It
If your project dynamically changes this
scopes around in a way TypeScript has difficulties modeling, this rule may not be viable to use.
One likely difficult pattern is if your code intentionally waits to bind methods after use, such as by passing a scope: this
along with the method.
You might consider using ESLint disable comments for those specific situations instead of completely disabling this rule.
If you're wanting to use toBeCalled
and similar matches in jest
tests, you can disable this rule for your test files in favor of eslint-plugin-jest
's version of this rule.
Type checked lint rules are more powerful than traditional lint rules, but also require configuring type checked linting. See Performance Troubleshooting if you experience performance degredations after enabling type checked rules.