core/quality/eslint/no-fake-abstract-this.eslint.rule.ts
core/quality/eslint/no-fake-abstract-this.eslint.rule.ts is a file in GovLab Quality. 60 lines of code and 8 definitions.
import type { Rule } from "eslint";
import { govlabMeta } from "#core/factories/eslint.factory";
interface AstNode {
type: string;
operator?: string;
argument?: unknown;
left?: unknown;
right?: unknown;
}
const COMPARISON_OPERATORS = new Set(["==", "!=", "===", "!=="]);
const isAst = function isAst(value: unknown): value is AstNode {
return value !== null && typeof value === "object" && "type" in value;
};
const isThis = function isThis(value: unknown): boolean {
return isAst(value) && value.type === "ThisExpression";
};
const testsThis = function testsThis(test: unknown): boolean {
if (isThis(test)) {
return true;
}
if (!isAst(test)) {
return false;
}
if (test.type === "UnaryExpression" && test.operator === "!") {
return isThis(test.argument);
}
if (
test.type === "BinaryExpression" &&
typeof test.operator === "string" &&
COMPARISON_OPERATORS.has(test.operator)
) {
return isThis(test.left) || isThis(test.right);
}
return false;
};
export default {
create(context: Rule.RuleContext): Rule.RuleListener {
const onIf = (node: Rule.Node): void => {
if (node.type !== "IfStatement") {
return;
}
if (testsThis(node.test)) {
context.report({ messageId: "fakeAbstract", node: node.test });
}
};
const handlers: [string, (node: Rule.Node) => void][] = [["IfStatement", onIf]];
return Object.fromEntries(handlers);
},
meta: govlabMeta({
canonical: ["design-by-contract"],
description:
"Disallow branching on `this` (`if (this)`, `!this`, `this == null`) — an always-constant condition used to fake a `this`-use and dodge class-methods-use-this in a throw-based fake-abstract base; model the contract as an interface + factory instead",
messages: {
fakeAbstract:
"Branching on `this` is always constant inside a method and fakes a `this`-use to dodge class-methods-use-this; replace the fake-abstract base class with an interface + factory contract.",
},
ruleId: "no_fake_abstract_this",
type: "problem",
}),
} satisfies Rule.RuleModule;