| 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.
context.setFilePath(filePath);
const reporter = new Reporter(config);
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.
function resolvePattern(pattern: string): string {
if (typeof pattern !== 'string') {
throw new Error('pattern must be a string');
}
return glob(pattern);
}
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.
const name = (config as any).testName;
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.
function formatTestName(name: string): string {
return name.replace(/\.test$/, '');
}
const display = formatTestName(file);
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.
try {
await loadConfig(path);
} catch (error) {
throw new Error(
`Failed to load: ${error instanceof Error ? error.message : error}`,
);
}
try {
await loadConfig(path);
} catch (error) {
throw new Error(`Failed to load config at ${path}`, { cause: error });
}
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.