with one click
typescript-standards
// TypeScript coding standards. Use when writing TypeScript, reviewing code, or refactoring. Enforces named exports, no dynamic imports, discriminated unions, proper type safety.
// TypeScript coding standards. Use when writing TypeScript, reviewing code, or refactoring. Enforces named exports, no dynamic imports, discriminated unions, proper type safety.
CRITICAL - Invoke BEFORE writing ANY test code. Use when creating new tests, adding test cases, modifying existing tests, writing `it()` or `describe()` blocks, or touching any `*.test.ts` or `*.spec.ts` file. Enforces no try-catch in positive tests, no early returns, no test skipping.
Environment variable management patterns. CRITICAL use when adding new environment variables (secrets, API keys, config), debugging "X not defined" or missing env var errors, tests passing locally but failing in CI, Turborepo not passing env vars to tasks, or troubleshooting deployment configuration errors.
Test the local Rudel API with authenticated requests. Use when the user wants to test API endpoints, debug RPC calls, verify auth flows, or inspect API responses against the local dev server.
Use when creating a pull request. Runs verification, reviews changes, and creates the PR with correct metadata. Invoke BEFORE pushing code.
Query ClickHouse databases using the chcli CLI tool. Use when the user wants to run SQL queries against ClickHouse, explore database schemas, inspect tables, or extract data from ClickHouse.
Code architecture patterns. Use when organizing code, refactoring classes, designing service structure, or extracting/moving code to new files. Enforces function ordering, service functions over classes, dependency injection.
| name | typescript-standards |
| description | TypeScript coding standards. Use when writing TypeScript, reviewing code, or refactoring. Enforces named exports, no dynamic imports, discriminated unions, proper type safety. |
| allowed-tools | ["Read","Edit","Grep","Glob"] |
// ❌ Bad
export default function myFunction() {}
// ✅ Good
export function myFunction() {}
Only export what other packages actually consume:
// ❌ Bad - Exports everything including internal types
export * from "./digest";
// ✅ Good - Explicit public interface
export {
fetchCompoundDigest,
buildCompoundDigestBlocks,
type CompoundDigestEnv,
} from "./digest";
Before adding exports, check what's actually imported by consumer packages.
Always use static imports at the top of the file. Only use dynamic imports (await import()) when:
Never use dynamic imports just to import something mid-function when a static import would work.
For values used only as types, use explicit type-only imports:
// ❌ Bad - Imports value but only uses type
import { MyClass } from './my-class'
type Instance = MyClass
// ✅ Good - Clear intent, no runtime import
import { type MyClass } from './my-class'
type Instance = MyClass
my-component.ts)myVariable, myFunction())MyClass, MyInterface)MAX_COUNT)T (TKey, TValue)When a Zod schema exists, always derive the TypeScript type from it using z.infer. Never define a separate interface that duplicates the schema's shape:
// ❌ Bad - Manually duplicates the Zod schema, drifts over time
export interface User {
id: string
name: string
email: string
}
// ✅ Good - Single source of truth
import { type User } from "@rudel/api-routes"
// ✅ Good - Extend when the service needs extra internal fields
import { type User as UserBase } from "@rudel/api-routes"
export interface User extends UserBase {
session_count: number
}
If the type needs fields beyond the schema, extend the inferred type rather than redefining it.
Never use as or as unknown as type assertions. They hide type incompatibilities:
// ❌ Bad - Hides the real problem
const env = context.env as unknown as ExecutorEnv
// ✅ Good - Fix the underlying type mismatch
// Option 1: Properly type the source
const env: ExecutorEnv = context.env
// Option 2: Use type guards for runtime checking
function isExecutorEnv(env: unknown): env is ExecutorEnv {
return typeof env === 'object' && env !== null && 'DB' in env
}
If you encounter code that seems to require a cast:
// ✅ Good - Prevents impossible states
type FetchingState<TData> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: TData }
| { status: 'error'; error: Error }
// Handle with switch
switch (state.status) {
case 'success':
console.log(state.data)
break
}
Use sparingly. Prefer T | undefined over T? to force explicit passing:
// ✅ Good
type AuthOptions = {
userId: string | undefined
}
// ❌ Bad - & operator has terrible performance
type C = A & B
// ✅ Good
interface C extends A, B {}
Use as const objects instead:
const sizes = {
xs: 'EXTRA_SMALL',
sm: 'SMALL',
} as const
type Size = keyof typeof sizes
Never rely on training data for versions. Always install latest:
pnpm add -D @typescript-eslint/eslint-plugin
Use Promise.all for parallel operations:
const [users, posts] = await Promise.all([fetchUsers(), fetchPosts()])