# core/quality/eslint/no-chained-assignment.eslint.rule.ts

> 57 lines of code and 17 definitions.

Tree: GovLab Quality
Language: typescript
Canonical: https://banes-lab.com/anatomy/quality#file-quality-core-quality-eslint-no-chained-assignment-eslint-rule-ts
Source text: https://banes-lab.com/source/quality/core/quality/eslint/no-chained-assignment.eslint.rule.ts.txt

Listed in [core/quality/eslint](https://banes-lab.com/api/source/quality/core/quality/eslint.md), after [core/quality/eslint/no-call-enumeration.eslint.rule.ts](https://banes-lab.com/source/quality/core/quality/eslint/no-call-enumeration.eslint.rule.ts.md) and before [core/quality/eslint/no-concrete-dependency.eslint.rule.ts](https://banes-lab.com/source/quality/core/quality/eslint/no-concrete-dependency.eslint.rule.ts.md).

## Definitions

- `splitChain` (lexical_declaration, line 24)
- `onAssign` (lexical_declaration, line 37, exported)
- `AssignExpr` (type_alias_declaration, line 4)
- `indentOf` (lexical_declaration, line 6)
- `line` (lexical_declaration, line 7)
- `collectTargets` (lexical_declaration, line 11)
- `targets` (lexical_declaration, line 15)
- `current` (lexical_declaration, line 16)
- `{ rhs, targets }` (lexical_declaration, line 25)
- `indent` (lexical_declaration, line 26)
- `lines` (lexical_declaration, line 27)
- `i` (lexical_declaration, line 28)
- `source` (lexical_declaration, line 29)
- `create` (method_definition, line 36, exported)
- `statement` (lexical_declaration, line 41, exported)
- `fixable` (lexical_declaration, line 42, exported)
- `handlers` (lexical_declaration, line 51, exported)

## Contained in

- [core/quality/eslint](https://banes-lab.com/anatomy/quality/folder-quality-core-quality-eslint.md)

## Linked from

- [core/quality/eslint](https://banes-lab.com/anatomy/quality/folder-quality-core-quality-eslint.md)

## Source

```typescript
import type { Rule } from "eslint";
import { govlabMeta } from "#core/factories/eslint.factory";

type AssignExpr = Extract<Rule.Node, { type: "AssignmentExpression" }>;

const indentOf = function indentOf(context: Rule.RuleContext, node: Rule.Node): string {
    const line = context.sourceCode.lines[(node.loc?.start.line ?? 1) - 1] ?? "";
    return line.slice(0, line.length - line.trimStart().length);
};

const collectTargets = function collectTargets(
    context: Rule.RuleContext,
    node: AssignExpr,
): { targets: string[]; rhs: string } {
    const targets = [context.sourceCode.getText(node.left)];
    let current = node.right;
    while (current.type === "AssignmentExpression") {
        targets.push(context.sourceCode.getText(current.left));
        current = current.right;
    }
    return { rhs: context.sourceCode.getText(current), targets };
};

const splitChain = function splitChain(context: Rule.RuleContext, node: AssignExpr): string {
    const { rhs, targets } = collectTargets(context, node);
    const indent = indentOf(context, node);
    const lines: string[] = [];
    for (let i = targets.length - 1; i >= 0; i -= 1) {
        const source = i + 1 < targets.length ? targets[i + 1] : rhs;
        lines.push(`${targets[i]} = ${source};`);
    }
    return lines.join(`\n${indent}`);
};

export default {
    create(context: Rule.RuleContext): Rule.RuleListener {
        const onAssign = (node: Rule.Node): void => {
            if (node.type !== "AssignmentExpression" || node.right.type !== "AssignmentExpression") {
                return;
            }
            const statement = node.parent;
            const fixable = statement.type === "ExpressionStatement";
            context.report({
                ...(fixable
                    ? { fix: (fixer): Rule.Fix => fixer.replaceText(statement, splitChain(context, node)) }
                    : {}),
                messageId: "unexpected",
                node,
            });
        };
        const handlers: [string, (node: Rule.Node) => void][] = [["AssignmentExpression", onAssign]];
        return Object.fromEntries(handlers);
    },
    meta: govlabMeta({
        canonical: ["idiomatic-preference"],
        description: "Disallow chained assignment (`a = b = c`); split into sequential statements",
        fixable: "code",
        messages: { unexpected: "Chained assignment is not allowed; assign each target in its own statement." },
        ruleId: "no_chained_assignment",
        type: "suggestion",
    }),
} satisfies Rule.RuleModule;
```
