mirror of
https://github.com/luxexchange/eslint-config.git
synced 2026-10-30 18:13:47 +00:00
111 lines
3.3 KiB
JavaScript
111 lines
3.3 KiB
JavaScript
'use strict'
|
|
|
|
const { getParserServices } = require('@typescript-eslint/utils/eslint-utils')
|
|
const ts = require('typescript')
|
|
|
|
/**
|
|
* Check if a TypeScript method declaration contains a 'this' keyword.
|
|
*/
|
|
function methodUsesThis(tsMethod) {
|
|
let found = false
|
|
function visit(node) {
|
|
if (node.kind === ts.SyntaxKind.ThisKeyword) {
|
|
found = true
|
|
return
|
|
}
|
|
ts.forEachChild(node, visit)
|
|
}
|
|
if (tsMethod.body) {
|
|
visit(tsMethod.body)
|
|
}
|
|
return found
|
|
}
|
|
|
|
/**
|
|
* This rule works for destructured methods from local classes/objects, even if imported,
|
|
* as long as their source is available to the TypeScript program. It will not work for
|
|
* node_modules or .d.ts files (library code).
|
|
*/
|
|
|
|
module.exports = {
|
|
meta: {
|
|
type: 'problem',
|
|
docs: {
|
|
description: "Prevent destructuring methods that use 'this' which can lead to context loss",
|
|
category: 'Possible Errors',
|
|
recommended: true,
|
|
},
|
|
fixable: undefined, // This rule isn't automatically fixable
|
|
schema: [],
|
|
messages: {
|
|
preventDestructure:
|
|
"Destructuring method '{{name}}' will cause it to lose 'this' context. Use object.{{name}}() instead.",
|
|
},
|
|
},
|
|
create(context) {
|
|
const parserServices = getParserServices(context)
|
|
const typeChecker = parserServices.program.getTypeChecker()
|
|
|
|
return {
|
|
"VariableDeclarator[id.type='ObjectPattern']"(node) {
|
|
const objectNode = node.init
|
|
if (!objectNode) {
|
|
return
|
|
}
|
|
|
|
try {
|
|
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(objectNode)
|
|
const type = typeChecker.getTypeAtLocation(tsNode)
|
|
const properties = type.getProperties()
|
|
|
|
for (const prop of node.id.properties) {
|
|
if (prop.type !== 'Property') {
|
|
continue
|
|
}
|
|
const propertyName = prop.key.name
|
|
const symbol = properties.find((s) => s.getName() === propertyName)
|
|
if (!symbol) {
|
|
continue
|
|
}
|
|
|
|
const declarations = symbol.getDeclarations() || []
|
|
for (const decl of declarations) {
|
|
// Check method declarations (method() { ... })
|
|
if (
|
|
ts.isMethodDeclaration(decl) &&
|
|
decl.body &&
|
|
!decl.getSourceFile().isDeclarationFile &&
|
|
methodUsesThis(decl)
|
|
) {
|
|
context.report({
|
|
node: prop,
|
|
messageId: 'preventDestructure',
|
|
data: { name: propertyName },
|
|
})
|
|
break
|
|
}
|
|
// Check property assignments with function expressions (method: function() { ... })
|
|
if (
|
|
ts.isPropertyAssignment(decl) &&
|
|
ts.isFunctionExpression(decl.initializer) &&
|
|
decl.initializer.body &&
|
|
!decl.getSourceFile().isDeclarationFile &&
|
|
methodUsesThis(decl.initializer)
|
|
) {
|
|
context.report({
|
|
node: prop,
|
|
messageId: 'preventDestructure',
|
|
data: { name: propertyName },
|
|
})
|
|
break
|
|
}
|
|
}
|
|
}
|
|
} catch {
|
|
// Handle type resolution errors
|
|
}
|
|
},
|
|
}
|
|
},
|
|
}
|