feat(rules): add initial rule files for concerns, languages, and frameworks

Concerns (6 files):
- coding-style.md (163 lines): patterns, anti-patterns, error handling, SOLID
- naming.md (105 lines): naming conventions table per language
- documentation.md (149 lines): docstrings, WHY vs WHAT, README standards
- testing.md (134 lines): AAA pattern, mocking philosophy, TDD
- git-workflow.md (118 lines): conventional commits, branch naming, PR format
- project-structure.md (82 lines): directory layout, entry points, config placement

Languages (4 files):
- python.md (224 lines): uv, ruff, pyright, pytest, pydantic, idioms, anti-patterns
- typescript.md (150 lines): strict mode, discriminated unions, satisfies, as const
- nix.md (129 lines): flake structure, module patterns, alejandra, anti-patterns
- shell.md (100 lines): set -euo pipefail, shellcheck, quoting, POSIX

Frameworks (1 file):
- n8n.md (42 lines): workflow design, node patterns, Error Trigger, security

Context budget: 975 lines (concerns + python) < 1500 limit

Refs: T6-T16 of rules-system plan
This commit is contained in:
m3tm3re
2026-02-17 19:05:45 +01:00
parent d475dde398
commit 8910413315
12 changed files with 1407 additions and 11 deletions

View File

@@ -0,0 +1,150 @@
# TypeScript Patterns
## Strict tsconfig
Always enable strict mode and key safety options:
```json
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true
}
}
```
## Discriminated Unions
Use discriminated unions for exhaustive type safety:
```ts
type Result =
| { success: true; data: string }
| { success: false; error: Error };
function handleResult(result: Result): string {
if (result.success) {
return result.data;
}
throw result.error;
}
```
## Branded Types
Prevent type confusion with nominal branding:
```ts
type UserId = string & { readonly __brand: unique symbol };
type Email = string & { readonly __brand: unique symbol };
function createUserId(id: string): UserId {
return id as UserId;
}
function sendEmail(email: Email, userId: UserId) {}
```
## satisfies Operator
Use `satisfies` for type-safe object literal inference:
```ts
const config = {
port: 3000,
host: "localhost",
} satisfies {
port: number;
host: string;
debug?: boolean;
};
config.port; // number
config.host; // string
```
## as const Assertions
Freeze literal types with `as const`:
```ts
const routes = {
home: "/",
about: "/about",
contact: "/contact",
} as const;
type Route = typeof routes[keyof typeof routes];
```
## Modern Features
```ts
// Promise.withResolvers()
const { promise, resolve, reject } = Promise.withResolvers<string>();
// Object.groupBy()
const users = [
{ name: "Alice", role: "admin" },
{ name: "Bob", role: "user" },
];
const grouped = Object.groupBy(users, u => u.role);
// using statement for disposables
class Resource implements Disposable {
async [Symbol.asyncDispose]() {
await this.cleanup();
}
}
async function withResource() {
using r = new Resource();
}
```
## Toolchain
Prefer modern tooling:
- Runtime: `bun` or `tsx` (no `tsc` for execution)
- Linting: `biome` (preferred) or `eslint`
- Formatting: `biome` (built-in) or `prettier`
## Anti-Patterns
Avoid these TypeScript patterns:
```ts
// NEVER use as any
const data = response as any;
// NEVER use @ts-ignore
// @ts-ignore
const value = unknownFunction();
// NEVER use ! assertion (non-null)
const element = document.querySelector("#foo")!;
// NEVER use enum (prefer union)
enum Status { Active, Inactive } // ❌
// Prefer const object or union
type Status = "Active" | "Inactive"; // ✅
const Status = { Active: "Active", Inactive: "Inactive" } as const; // ✅
```
## Indexed Access Safety
With `noUncheckedIndexedAccess`, handle undefined:
```ts
const arr: string[] = ["a", "b"];
const item = arr[0]; // string | undefined
const item2 = arr.at(0); // string | undefined
const map = new Map<string, number>();
const value = map.get("key"); // number | undefined
```