typescript-strictness
Skill0 starsChanged 2 months agoMITView on GitHub
Design TypeScript types that catch bugs before runtime — narrowing, discriminated unions, branded types, generics that pull their weight. Use when writing types, refactoring any-typed code, or making `tsc --strict` pass without lies.
In this repository11 other files it gives its agents.
- api-contract-designSkill
- browser-debuggingSkill
- component-reviewSkill
- css-architectureSkill
- frontend-testing-strategySkill
- nextjs-performanceSkill
- performance-budgetSkill
- react-hooks-disciplineSkill
- state-managementSkill
- vue3-compositionSkill
- web-a11ySkill
The file
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 1 is distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 68
Use at module boundaries (API clients, DB access) where mix-ups are catastrophic and cheap to prevent.
- Very specific: line 79
If a real any remains, quarantine it in one adapter function with a comment stating the invariant the compiler can't see.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
TypeScript strictness that pays rent
Model the impossible states out of existence
Discriminated unions over boolean soup:
// ❌ 4 boolean fields, 16 combos, 12 impossible
interface Request {
loading: boolean
error?: Error
data?: User[]
refreshing: boolean
}
// ✅ 3 combos, all legal
type Request =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'error'; error: Error }
| { status: 'success'; data: User[]; fetchedAt: number }Switch on the discriminant and never-check exhaustiveness:
function render(r: Request) {
switch (r.status) {
case 'idle': return null
case 'loading': return <Spinner/>
case 'error': return <Error err={r.error}/>
case 'success': return <List data={r.data}/>
default: {
const _exhaustive: never = r
return _exhaustive
}
}
}Narrow, don't assert
assert/assilences the compiler;satisfies/guards teach it:
function isUser(x: unknown): x is User {
return typeof x === 'object' && x !== null && 'id' in x
}satisfieskeeps inference while checking shape:const config = {...} satisfies Config.
Branded types at the boundaries
IDs, emails, money amounts are not string/number:
type UserId = string & { readonly __brand: 'UserId' }
const asUserId = (s: string) => s as UserId
// loadUser(string) no longer compiles with an order idUse at module boundaries (API clients, DB access) where mix-ups are catastrophic and cheap to prevent.
Generics: only when the caller gains information
- A generic that appears once and is unrelated to other params is noise.
- Useful generics correlate inputs/outputs:
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>.
any triage
unknown+ narrowing for external input.as constfor literal maps and route tables.- If a real
anyremains, quarantine it in one adapter function with a comment stating the invariant the compiler can't see.
Migration playbook (strict on an existing repo)
tsc --strict→ list errors by rule.- Fix
strictNullChecksfirst (biggest payoff), thennoImplicitAny. - For legacy seas of any, add
// @ts-expect-errorwith reason — it errors when the code becomes correct, unlike plain suppression.
Definition of done (agent checklist)
- No new
any;unknownat external boundaries - Unions use discriminants; exhaustiveness via
never - IDs/emails/money branded at boundaries
- No type assertions across unrelated types
- Public function params/returns explicitly typed
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

