

Also from Kynth Studios


Also from Kynth Studios


Also from Kynth Studios
123456# TypeScript Type Safety78## Overview910**Zero tolerance for `any` types.** Every `any` is a runtime bug waiting to happen.1112Replace `any` with proper types using interfaces, `unknown` with type guards, or generic constraints. Use `@ts-expect-error` with explanation only when absolutely necessary.1314## When to Use1516**Use when you see:**17- `: any` in function parameters or return types18- `as any` type assertions19- TypeScript errors you're tempted to ignore20- External libraries without proper types21- Catch blocks with implicit `any`2223**Don't use for:**24- Already properly typed code25- Third-party `.d.ts` files (contribute upstream instead)2627## Type Safety Hierarchy2829**Prefer in this order:**301. Explicit interface/type definition312. Generic type parameters with constraints323. Union types334. `unknown` (with type guards)345. `never` (for impossible states)3536**Never use:** `any`3738## Quick Reference3940| Pattern | Bad | Good |41|---------|-----|------|42| **Error handling** | `catch (error: any)` | `catch (error) { if (error instanceof Error) ... }` |43| **Unknown data** | `JSON.parse(str) as any` | `const data = JSON.parse(str); if (isValid(data)) ...` |44| **Type assertions** | `(request as any).user` | `(request as AuthRequest).user` |45| **Double casting** | `return data as unknown as Type` | Align interfaces instead: make types compatible |46| **External libs** | `const server = fastify() as any` | `declare module 'fastify' { ... }` |47| **Generics** | `function process(data: any)` | `function process<T extends Record<string, unknown>>(data: T)` |4849## Implementation5051### Error Handling5253```typescript54// ❌ BAD55try {56 await operation();57} catch (error: any) {58 console.error(error.message);59}6061// ✅ GOOD - Use unknown and type guard62try {63 await operation();64} catch (error) {65 if (error instanceof Error) {66 console.error(error.message);67 } else {68 console.error('Unknown error:', String(error));69 }70}7172// ✅ BETTER - Helper function73function toError(error: unknown): Error {74 if (error instanceof Error) return error;75 return new Error(String(error));76}7778try {79 await operation();80} catch (error) {81 const err = toError(error);82 console.error(err.message);83}84```8586### Unknown Data Validation8788```typescript89// ❌ BAD90const data = await response.json() as any;91console.log(data.user.name);9293// ✅ GOOD - Type guard94interface UserResponse {95 user: {96 name: string;97 email: string;98 };99}100101function isUserResponse(data: unknown): data is UserResponse {102 return (103 typeof data === 'object' &&104 data !== null &&105 'user' in data &&106 typeof data.user === 'object' &&107 data.user !== null &&108 'name' in data.user &&109 typeof data.user.name === 'string'110 );111}112113const data = await response.json();114if (isUserResponse(data)) {115 console.log(data.user.name); // Type-safe116}117```118119### Module Augmentation120121```typescript122// ❌ BAD123const user = (request as any).user;124const db = (server as any).pg;125126// ✅ GOOD - Augment third-party types127import { FastifyRequest, FastifyInstance } from 'fastify';128129interface AuthUser {130 user_id: string;131 username: string;132 email: string;133}134135declare module 'fastify' {136 interface FastifyRequest {137 user?: AuthUser;138 }139140 interface FastifyInstance {141 pg: PostgresPlugin;142 }143}144145// Now type-safe everywhere146const user = request.user; // AuthUser | undefined147const db = server.pg; // PostgresPlugin148```149150### Generic Constraints151152```typescript153// ❌ BAD154function merge(a: any, b: any): any {155 return { ...a, ...b };156}157158// ✅ GOOD - Constrained generic159function merge<160 T extends Record<string, unknown>,161 U extends Record<string, unknown>162>(a: T, b: U): T & U {163 return { ...a, ...b };164}165```166167### Type Alignment (Avoid Double Casts)168169```typescript170// ❌ BAD - Double cast indicates misaligned types171interface SearchPackage {172 id: string;173 type: string; // Too loose174}175176interface RegistryPackage {177 id: string;178 type: PackageType; // Specific enum179}180181return data.packages as unknown as RegistryPackage[]; // Hiding incompatibility182183// ✅ GOOD - Align types from the source184interface SearchPackage {185 id: string;186 type: PackageType; // Use same specific type187}188189interface RegistryPackage {190 id: string;191 type: PackageType; // Now compatible192}193194return data.packages; // No cast needed - types match195```196197**Rule:** If you need `as unknown as Type`, your interfaces are misaligned. Fix the root cause, don't hide it with double casts.198199## Common Mistakes200201| Mistake | Why It Fails | Fix |202|---------|--------------|-----|203| Using `any` for third-party libs | Loses all type safety | Use module augmentation or `@types/*` package |204| `as any` for complex types | Hides real type errors | Create proper interface or use `unknown` |205| `as unknown as Type` double casts | Misaligned interfaces | Align types at source - same enums/unions |206| Skipping catch block types | Unsafe error access | Use `unknown` with type guards or toError helper |207| Generic functions without constraints | Allows invalid operations | Add `extends` constraint |208| Ignoring `ts-ignore` accumulation | Tech debt compounds | Fix root cause, use `@ts-expect-error` with comment |209210## TSConfig Strict Settings211212Enable all strict options for maximum type safety:213214```json215{216 "compilerOptions": {217 "strict": true,218 "noImplicitAny": true,219 "strictNullChecks": true,220 "strictFunctionTypes": true,221 "strictBindCallApply": true,222 "strictPropertyInitialization": true,223 "noImplicitThis": true,224 "noUnusedLocals": true,225 "noUnusedParameters": true,226 "noImplicitReturns": true,227 "noFallthroughCasesInSwitch": true228 }229}230```231232## Type Audit Workflow2332341. **Find**: `grep -r ": any\|as any" --include="*.ts" src/`2352. **Categorize**: Group by pattern (errors, requests, external libs)2363. **Define**: Create interfaces/types for each category2374. **Replace**: Systematic replacement with proper types2385. **Validate**: `npm run build` must succeed2396. **Test**: All tests must pass240241## Real-World Impact242243**Before type safety:**244- Runtime errors from undefined properties245- Silent failures from type mismatches246- Hours debugging production issues247- Difficult refactoring248249**After type safety:**250- Errors caught at compile time251- IntelliSense shows all available properties252- Confident refactoring with compiler help253- Self-documenting code254255---256257**Remember:** Type safety isn't about making TypeScript happy - it's about preventing runtime bugs. Every `any` you eliminate is a production bug you prevent.258
One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| pr-pm/prpm.cursor/rules/testing-patterns.mdc · 121 | Cursor rules | testlint-formatstyletesting-strategy | 77/100 | 14 days ago | |
| pr-pm/prpm.cursor/rules/beanstalk-deploy.mdc · 121 | Cursor rules | teststyletypes | 62/100 | 14 days ago | |
| pr-pm/prpm.cursor/rules/core-principles.mdc · 121 | Cursor rules | testlint-formatstylearch+6 | 69/100 | 14 days ago | |
| pr-pm/prpm.cursor/rules/creating-agents-md.mdc · 121 | Cursor rules | testlint-formatstylearch+7 | 92/100 | 14 days ago | |
| pr-pm/prpm.cursor/rules/creating-cursor-rules.mdc · 121 | Cursor rules | testlint-formatstylearch+5 | 76/100 | 14 days ago | |
| pr-pm/prpm.cursor/rules/creating-skills.mdc · 121 | Cursor rules | stylearchtesting-strategydo-not+1 | 61/100 | 14 days ago | |
| pr-pm/prpm.cursor/rules/github-actions-testing.mdc · 121 | Cursor rules | setupbuildstylearch+4 | 93/100 | 14 days ago | |
| pr-pm/prpm.cursor/rules/karen-repo-reviewer.mdc · 121 | Cursor rules | archgit | 58/100 | 14 days ago | |
| pr-pm/prpm.cursor/rules/prpm-json-best-practices.mdc · 121 | Cursor rules | setuplint-formatstylearch+5 | 73/100 | 14 days ago | |
| pr-pm/prpm.cursor/rules/self-improve-cursor.mdc · 121 | Cursor rules | setuptestarchdependencies+3 | 69/100 | 14 days ago | |
| pr-pm/prpm.cursor/rules/typescript-type-specialist.mdc · 121 | Cursor rules | styletypesdo-notagent-behaviour | 65/100 | 14 days ago | |
| pr-pm/prpmAGENTS.md · 121 | AGENTS.md | setupbuildtestlint-format+12 | 84/100 | 14 days ago | |
| pr-pm/prpmCLAUDE.md · 121 | CLAUDE.md | teststylegitapi+2 | 69/100 | 14 days ago | |
| pr-pm/prpm.cursor/rules/creating-kiro-agents.mdc · 121 | Cursor rules | setupbuildteststyle+5 | 76/100 | 14 days ago | |
| pr-pm/prpm.cursor/rules/format-conversion.mdc · 121 | Cursor rules | testlint-formatstyledo-not+1 | 63/100 | 14 days ago |
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126 | Cursor rules | setupbuildtestlint-format+6 | 100/100 | 14 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 46 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 14 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 14 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 14 days ago | |
| bybren-llc/safe-agentic-workflow.cursor/rules/10-backend-python.mdc · 399 | Cursor rules | testlint-formatstylegit+4 | 97/100 | today |
A badge carrying the measured quality of the strongest agent config file in this repository, out of 100. It reads from this index every time somebody loads your page, so it changes when the measurement changes and there is nothing to keep up to date. Free, no account, and the value is not something you or we can set by hand.
[](https://rulestack.kynth.studio/configs/pr-pm-prpm-cursor-rules-typescript-type-safety)Would rather not hotlink us? Every badge is also served in shields.io’s endpoint schema, so shields renders the image and your readers never talk to our domain:
Published by Toolproof, the masthead over this index and eight others. The method behind the number is at toolproof.kynth.studio/methodology, and the whole thing is readable as JSON with no key at /api.