β¬ οΈ Back to Table of Contents
π prefer-for-of¶
π Analysis Summary¶
| Metric | Count |
|---|---|
| π§ Functions | 9 |
| π¦ Imports | 5 |
π Table of Contents¶
π οΈ File Location:¶
π packages/eslint-plugin/src/rules/prefer-for-of.ts
π€ Default Export¶
| Property | Value |
|---|---|
name |
'prefer-for-of' |
meta.type |
'suggestion' |
meta.docs.description |
'Enforce the use of for-of loop over the standard for loop where possible' |
meta.docs.recommended |
'stylistic' |
meta.messages.preferForOf |
'Expected a for-of loop instead of a for loop with this simple iteration.' |
meta.schema |
[] |
defaultOptions |
[] |
Entry point: create β documented under Functions.
π¦ Imports¶
| Name | Source |
|---|---|
TSESLint |
@typescript-eslint/utils |
TSESTree |
@typescript-eslint/utils |
AST_NODE_TYPES |
@typescript-eslint/utils |
createRule |
../util |
isAssignee |
../util |
Functions¶
create(context: any): { 'ForStatement:exit'(node: TSESTree.ForStatement): void; }¶
Parameters:
contextany
Returns: { 'ForStatement:exit'(node: TSESTree.ForStatement): void; }
Calls:
isLiteralisMatchingIdentifiercontext.sourceCode.getTextindexVar.references.everycontainsisAssignee (from ../util)isSingleVariableDeclarationisZeroInitializedisLessThanLengthExpressioncontext.sourceCode.getDeclaredVariablesisIncrementisIndexOnlyUsedWithArraycontext.report
Internal Comments:
Code
create(context) {
function isSingleVariableDeclaration(
node: TSESTree.Node | null,
): node is TSESTree.VariableDeclaration {
return (
node?.type === AST_NODE_TYPES.VariableDeclaration &&
node.kind !== 'const' &&
node.declarations.length === 1
);
}
function isLiteral(
node: TSESTree.Expression | TSESTree.PrivateIdentifier,
value: number,
): boolean {
return node.type === AST_NODE_TYPES.Literal && node.value === value;
}
function isZeroInitialized(node: TSESTree.VariableDeclarator): boolean {
return node.init != null && isLiteral(node.init, 0);
}
function isMatchingIdentifier(
node: TSESTree.Expression | TSESTree.PrivateIdentifier,
name: string,
): boolean {
return node.type === AST_NODE_TYPES.Identifier && node.name === name;
}
function isLessThanLengthExpression(
node: TSESTree.Node | null,
name: string,
): TSESTree.Expression | null {
if (
node?.type === AST_NODE_TYPES.BinaryExpression &&
node.operator === '<' &&
isMatchingIdentifier(node.left, name) &&
node.right.type === AST_NODE_TYPES.MemberExpression &&
isMatchingIdentifier(node.right.property, 'length')
) {
return node.right.object;
}
return null;
}
function isIncrement(node: TSESTree.Node | null, name: string): boolean {
if (!node) {
return false;
}
switch (node.type) {
case AST_NODE_TYPES.UpdateExpression:
// x++ or ++x
return (
node.operator === '++' && isMatchingIdentifier(node.argument, name)
);
case AST_NODE_TYPES.AssignmentExpression:
if (isMatchingIdentifier(node.left, name)) {
if (node.operator === '+=') {
// x += 1
return isLiteral(node.right, 1);
}
if (node.operator === '=') {
// x = x + 1 or x = 1 + x
const expr = node.right;
return (
expr.type === AST_NODE_TYPES.BinaryExpression &&
expr.operator === '+' &&
((isMatchingIdentifier(expr.left, name) &&
isLiteral(expr.right, 1)) ||
(isLiteral(expr.left, 1) &&
isMatchingIdentifier(expr.right, name)))
);
}
}
}
return false;
}
function contains(outer: TSESTree.Node, inner: TSESTree.Node): boolean {
return (
outer.range[0] <= inner.range[0] && outer.range[1] >= inner.range[1]
);
}
function isIndexOnlyUsedWithArray(
body: TSESTree.Statement,
indexVar: TSESLint.Scope.Variable,
arrayExpression: TSESTree.Expression,
): boolean {
const arrayText = context.sourceCode.getText(arrayExpression);
return indexVar.references.every(reference => {
const id = reference.identifier;
const node = id.parent;
return (
!contains(body, id) ||
(node.type === AST_NODE_TYPES.MemberExpression &&
node.object.type !== AST_NODE_TYPES.ThisExpression &&
node.property === id &&
context.sourceCode.getText(node.object) === arrayText &&
!isAssignee(node))
);
});
}
return {
'ForStatement:exit'(node: TSESTree.ForStatement): void {
if (!isSingleVariableDeclaration(node.init)) {
return;
}
const declarator = node.init.declarations[0] as
TSESTree.VariableDeclarator | undefined;
if (
!declarator ||
!isZeroInitialized(declarator) ||
declarator.id.type !== AST_NODE_TYPES.Identifier
) {
return;
}
const indexName = declarator.id.name;
const arrayExpression = isLessThanLengthExpression(
node.test,
indexName,
);
if (!arrayExpression) {
return;
}
const [indexVar] = context.sourceCode.getDeclaredVariables(node.init);
if (
isIncrement(node.update, indexName) &&
isIndexOnlyUsedWithArray(node.body, indexVar, arrayExpression)
) {
context.report({
node,
messageId: 'preferForOf',
});
}
},
};
}
Internal helpers¶
Declared inside another function in this file.
isSingleVariableDeclaration(node: TSESTree.Node | null): node is TSESTree.VariableDeclaration¶
Parameters:
nodeTSESTree.Node | null
Returns: node is TSESTree.VariableDeclaration
Code
isLiteral(node: TSESTree.Expression | TSESTree.PrivateIβ¦, value: number): boolean¶
Parameters:
nodeTSESTree.Expression | TSESTree.PrivateIdentifiervaluenumber
Returns: boolean
Code
isZeroInitialized(node: TSESTree.VariableDeclarator): boolean¶
Parameters:
nodeTSESTree.VariableDeclarator
Returns: boolean
Calls:
isLiteral
Code
isMatchingIdentifier(node: TSESTree.Expression | TSESTree.PrivateIβ¦, name: string): boolean¶
Parameters:
nodeTSESTree.Expression | TSESTree.PrivateIdentifiernamestring
Returns: boolean
Code
isLessThanLengthExpression(node: TSESTree.Node | null, name: string): TSESTree.Expression | null¶
Parameters:
nodeTSESTree.Node | nullnamestring
Returns: TSESTree.Expression | null
Calls:
isMatchingIdentifier
Code
function isLessThanLengthExpression(
node: TSESTree.Node | null,
name: string,
): TSESTree.Expression | null {
if (
node?.type === AST_NODE_TYPES.BinaryExpression &&
node.operator === '<' &&
isMatchingIdentifier(node.left, name) &&
node.right.type === AST_NODE_TYPES.MemberExpression &&
isMatchingIdentifier(node.right.property, 'length')
) {
return node.right.object;
}
return null;
}
isIncrement(node: TSESTree.Node | null, name: string): boolean¶
Parameters:
nodeTSESTree.Node | nullnamestring
Returns: boolean
Calls:
isMatchingIdentifierisLiteral
Internal Comments:
Code
function isIncrement(node: TSESTree.Node | null, name: string): boolean {
if (!node) {
return false;
}
switch (node.type) {
case AST_NODE_TYPES.UpdateExpression:
// x++ or ++x
return (
node.operator === '++' && isMatchingIdentifier(node.argument, name)
);
case AST_NODE_TYPES.AssignmentExpression:
if (isMatchingIdentifier(node.left, name)) {
if (node.operator === '+=') {
// x += 1
return isLiteral(node.right, 1);
}
if (node.operator === '=') {
// x = x + 1 or x = 1 + x
const expr = node.right;
return (
expr.type === AST_NODE_TYPES.BinaryExpression &&
expr.operator === '+' &&
((isMatchingIdentifier(expr.left, name) &&
isLiteral(expr.right, 1)) ||
(isLiteral(expr.left, 1) &&
isMatchingIdentifier(expr.right, name)))
);
}
}
}
return false;
}
contains(outer: TSESTree.Node, inner: TSESTree.Node): boolean¶
Parameters:
outerTSESTree.NodeinnerTSESTree.Node
Returns: boolean
Code
isIndexOnlyUsedWithArray(body: TSESTree.Statement, indexVar: TSESLint.Scope.Variable, arrayExpression: TSESTree.Expression): boolean¶
Parameters:
bodyTSESTree.StatementindexVarTSESLint.Scope.VariablearrayExpressionTSESTree.Expression
Returns: boolean
Calls:
context.sourceCode.getTextindexVar.references.everycontainsisAssignee (from ../util)
Code
function isIndexOnlyUsedWithArray(
body: TSESTree.Statement,
indexVar: TSESLint.Scope.Variable,
arrayExpression: TSESTree.Expression,
): boolean {
const arrayText = context.sourceCode.getText(arrayExpression);
return indexVar.references.every(reference => {
const id = reference.identifier;
const node = id.parent;
return (
!contains(body, id) ||
(node.type === AST_NODE_TYPES.MemberExpression &&
node.object.type !== AST_NODE_TYPES.ThisExpression &&
node.property === id &&
context.sourceCode.getText(node.object) === arrayText &&
!isAssignee(node))
);
});
}
Generated by Syntax Scribe