@rccyx/eslint-plugin-classes
v1.1.1
Published
Deterministic ES6 class discipline for humans and AI swarms
Downloads
553
Maintainers
Readme
@rccyx/eslint-plugin-classes
Large teams and AI-generated code both create the same problem: classes slowly rot. Members drift out of order, private internals leak into public surfaces, and scanning a file gets exhaustive. This rule forces a strict, predictable layout so intent is never ambiguous. It stabilizes structure across humans and machines and prevents the entropy that usually builds up in big TypeScript codebases.
Enforce a readable class layout with explicit visibility, consistent naming, and a fixed member order.
Install
pnpm i -D eslint @rccyx/eslint-plugin-classes
# or
bun add -d eslint @rccyx/eslint-plugin-classesPeer Dependencies
This plugin requires:
eslint:>=8.57 <11typescript:^5
Flat config usage
// eslint.config.js
import classStructure from "@rccyx/eslint-plugin-classes";
export default [
{
files: ["**/*.ts", "**/*.tsx"],
plugins: { "class-structure": classStructure },
rules: {
"class-structure/enforce": "error",
},
},
];What it enforces
1. Member ordering
Members must appear in this exact order:
- Static fields
- Static methods (including static accessors)
- Instance fields (including method-like fields:
foo = () => {}) - Constructor
- Instance accessors (
get/set) - Public instance methods
- Protected instance methods
- Private instance methods
// Good
class Example {
static readonly VERSION = "1.0";
static init() {}
static get meta() {
return "ok";
}
public id = "x";
private _cache = new Map();
constructor(id: string) {
this.id = id;
}
get state() {}
set state(v) {}
public run() {}
protected compute() {}
private _cleanup() {}
}2. Visibility requirements
All non-accessor instance methods and all method-like fields must explicitly declare visibility.
// Bad
class Bad {
foo() {} // error
handler = () => {}; // error
}
// Good
class Good {
public foo() {}
public handler = () => {};
}Exceptions:
- Constructors may omit visibility
- Accessors may omit visibility
- Static members may omit visibility
3. Naming rules
Private members
Private fields and methods must start with # or _.
// Bad
private hidden() {}
private value: number = 1;
// Good
private _hidden() {}
private #value: number;Public members
Public members must not start with _ or #.
// Bad
public _call() {}
_id = 1;
// Good
public call() {}
id = 1;Note: Accessors may start with _ without error.
Constants
Static readonly fields must be SCREAMING_SNAKE_CASE.
// Bad
static readonly defaultLimit = 3;
// Good
static readonly MAX_LIMIT = 3;
private static readonly _CACHE_SIZE = 10;4. Structural constraints
No fields after constructor
// Bad
class Bad {
constructor() {}
value = 1; // error
}No static/instance interleaving
Static members of a category must all appear before instance members of that category.
// Bad
static A = 1;
a = 2;
static B = 3; // errorClass must have behavior
A class must declare at least one method, accessor, or method-like field.
// Bad
class Bad {
value = 1; // no behavior
}What it flags
class Bad {
value = 1;
static init() {} // instance field before static method → error
foo() {} // visibility missing
private hidden() {} // private name must start with # or _
public _call() {} // public name must not start with _ or #
static readonly limit = 3; // constant naming error
constructor() {}
data = 2; // field after constructor → error
public run() {}
get name() {} // accessor after methods → error
}What passes
class Good {
static readonly VERSION = "1.0";
static init() {}
static get meta() {
return "ok";
}
public id = "x";
protected mode = 0;
private _cache = new Map<string, unknown>();
public handler = () => {};
constructor(id: string) {
this.id = id;
}
get state() {
return this.mode;
}
set state(v: number) {
this.mode = v;
}
public run() {}
protected compute() {}
private _cleanup() {}
}Notes
- Method-like fields (
foo = () => {}) behave like fields for ordering and methods for visibility. - Static accessors behave like static methods for ordering.
- Accessors may start with
_without violating public naming rules. - Hash-private members (
#name) automatically count as private.
License
Apache-2.0 © @rccyx
