- name
- typescript
- description
- TypeScript anti-slop guardrails. Use when writing or reviewing .ts, .tsx, and .mts files. Covers patterns that Biome, tsc, and rslint do not catch.
- metadata
- {"internal":true}
# TypeScript Anti-Slop Guardrails
Skip formatting and lint rules — tooling handles those. This skill targets patterns that produce valid-looking but poor-quality code.
When in doubt, **copy the local pattern in the file you are editing** rather than importing a style from elsewhere.
## 1. No Restating Comments
Comments that repeat what the next line does are noise. Only comment **why**, never **what**.
```typescript
// wrong
// Set the current file path
context.setFilePath(filePath);
// Initialize the reporter
const reporter = new Reporter(config);
// right — let code speak; comment only non-obvious intent
// Force sequential execution — parallel runs corrupt shared fixture state
context.setFilePath(filePath);
const reporter = new Reporter(config);
```
Also avoid: section-header comments inside functions (`// --- Validate ---`), JSDoc that adds nothing beyond the signature, temporal markers (`// added for`, `// Phase 1`).
## 2. No Unnecessary Defensive Code
Do not add runtime checks that the type system already guarantees. Trust the types.
```typescript
// wrong — param is already typed string
function resolvePattern(pattern: string): string {
if (typeof pattern !== 'string') {
throw new Error('pattern must be a string');
}
return glob(pattern);
}
// right — trust the type system
function resolvePattern(pattern: string): string {
return glob(pattern);
}
```
Also avoid: redundant `?? []` / `|| {}` on fields that are always initialized (normalize once at construction instead), null checks on values you just produced, `try/catch` around code that cannot throw.
## 3. Minimize `as`
Treat `as` as a last resort, not a way to silence errors. If you need `as`, the types are probably wrong.
```typescript
// wrong — casting to escape a type error
const name = (config as any).testName;
// right — narrow or fix the type
if ('testName' in config) {
const name = config.testName;
}
```
- Never `as any` or `as unknown as X` unless at a proven interop seam or test-only partial mock.
- If control flow already proved the type, the cast is redundant — remove it.
- When a cast is truly needed, add a one-line comment explaining why.
## 4. Minimize `any`
`noExplicitAny` is **off** in this repository, so self-police. Prefer `unknown` + narrowing, or a generic, before reaching for `any`. Never let `any` leak into exported APIs, config types, or cross-package contracts.
## 5. No One-Use Abstractions
Do not extract a function, class, or type that is used exactly once, unless it genuinely improves readability at the call site.
```typescript
// wrong — called once, five lines away
function formatTestName(name: string): string {
return name.replace(/\.test$/, '');
}
// ... later:
const display = formatTestName(file);
// right — inline it
const display = file.replace(/\.test$/, '');
```
Also avoid: interfaces with single implementations, factory functions that create one thing, options objects with 10 optional fields for features that don't exist yet.
**Threshold:** extract when used in 3+ places, or when the extraction meaningfully shortens a complex function.
## 6. No Catch-and-Rethrow Without Context
If a `catch` block doesn't add information or recover, delete the `try/catch`.
```typescript
// wrong — catches and rethrows with just a message rewrite
try {
await loadConfig(path);
} catch (error) {
throw new Error(
`Failed to load: ${error instanceof Error ? error.message : error}`,
);
}
// right — use cause to preserve the chain
try {
await loadConfig(path);
} catch (error) {
throw new Error(`Failed to load config at ${path}`, { cause: error });
}
// also right — just don't catch if you add nothing
await loadConfig(path);
```
Also avoid: bare rethrow (`catch (e) { throw e; }`), empty catch blocks, catch blocks that only `console.log`.
## 7. Single Source of Truth — Prevent Drift
When two declarations describe the same thing — types, constants, defaults, validation rules — one must derive from the other. Duplicating independently causes **drift**: they match today, diverge silently tomorrow. Derive via `extends`, `Pick`, `ReturnType`, `typeof`, `satisfies`, or simply importing the canonical definition.
## 8. Search Before Creating Utilities
Before writing a helper function, search the codebase. If it exists, import it. If it exists but isn't exported, export it. Don't create a second `isObject()` or `normalizeSlashes()`.
## 9. No Over-Engineered Types
- Don't introduce deep conditional types, mapped types, or template literal types unless they clearly reduce complexity.
- Don't create a reusable utility type for one call site.
- Prefer the smallest type shape that explains the contract.
- This repository uses both `type` and `interface` — follow nearby code.
## Repository Conventions
- Use `import type` for type-only imports.
- Public config properties: use JSDoc with `@default`.
- Preserve existing file-local patterns unless there is a clear bug or maintenance win.
View on GitHub