<< All versions
Skill v1.0.1
currentAutomated scan100/100b33eep/claude-code-setup/standards-typescript
3 files
──Details
PublishedMay 25, 2026 at 09:45 PM
Content Hashsha256:330c7d927835960c...
Git SHA7e8cc42fb508
Bump Typepatch
──Files
Files (1 file, 10.2 KB)
SKILL.md10.2 KBactive
SKILL.md · 360 lines · 10.2 KB
version: "1.0.1" name: standards-typescript description: This skill provides TypeScript coding standards and is automatically loaded for TypeScript projects. It includes naming conventions, best practices, and recommended tooling. type: context applies_to: [typescript, nodejs, express, nestjs, nextjs, react, vue, angular, deno, bun, zod] file_extensions: [".ts", ".tsx", ".jsx"]
TypeScript Coding Standards
Core Principles
- Simplicity: Simple, understandable code
- Readability: Readability over cleverness
- Maintainability: Code that's easy to maintain
- Testability: Code that's easy to test
- DRY: Don't Repeat Yourself - but don't overdo it
General Rules
- Early Returns: Use early returns to avoid nesting
- Descriptive Names: Meaningful names for variables and functions
- Minimal Changes: Only change relevant code parts
- No Over-Engineering: No unnecessary complexity
- Minimal Comments: Code should be self-explanatory. No redundant comments!
Naming Conventions
| Element | Convention | Example | |
|---|---|---|---|
| Variables/Functions | camelCase | getUserById, isActive | |
| Classes/Interfaces/Types | PascalCase | UserService, ApiClient | |
| Constants | UPPER_SNAKE_CASE | MAX_RETRY_COUNT | |
| Private | Prefix with _ or # | _internalMethod, #privateField | |
| Files | kebab-case or camelCase | user-service.ts, userService.ts | |
| Interfaces | No I prefix | User not IUser | |
| Type aliases | PascalCase | UserId, HttpMethod | |
| Event Handlers | Prefix with handle | handleClick, handleSubmit |
Project Structure
myproject/├── src/│ ├── index.ts # Entry point│ ├── config.ts # Settings, env vars│ ├── types/│ │ └── index.ts # Shared types│ ├── models/│ │ └── user.ts # Domain models│ ├── services/│ │ └── user-service.ts # Business logic│ ├── repositories/│ │ └── user-repo.ts # Data access│ └── utils/│ └── helpers.ts # Utility functions├── tests/│ ├── services/│ │ └── user-service.test.ts│ └── setup.ts├── package.json├── tsconfig.json└── README.md
Code Style
typescript
// Use explicit types for function parameters and return valuesfunction getUserById(userId: string): User | undefined {if (!userId) {throw new Error("userId cannot be empty");}// implementation...}// Prefer interfaces for object shapesinterface User {id: string;name: string;email: string;age?: number;}// Use type aliases for unions, intersections, or primitivestype UserId = string;type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";type Result<T> = { success: true; data: T } | { success: false; error: string };
Best Practices
typescript
// Prefer const over letconst users: User[] = [];// Use nullish coalescing and optional chainingconst name = user?.profile?.name ?? "Anonymous";// Prefer template literalsconst message = `Hello, ${user.name}!`;// Use destructuringconst { id, name, email } = user;function processUser({ id, name }: User): void { }// Prefer array methods over loopsconst activeUsers = users.filter(u => u.isActive);const userNames = users.map(u => u.name);const totalAge = users.reduce((sum, u) => sum + u.age, 0);// Use readonly for immutable datainterface Config {readonly apiUrl: string;readonly maxRetries: number;}// Use as const for literal typesconst DIRECTIONS = ["north", "south", "east", "west"] as const;type Direction = typeof DIRECTIONS[number];// Prefer unknown over anyfunction parseJson(input: string): unknown {return JSON.parse(input);}// Type guards for type narrowingfunction isUser(value: unknown): value is User {return typeof value === "object" && value !== null && "id" in value;}
Utility Types
typescript
// Partial<T> - Make all properties optionaltype UserUpdate = Partial<User>;// { id?: string; name?: string; email?: string; age?: number }// Pick<T, K> - Select specific propertiestype UserPreview = Pick<User, "id" | "name">;// { id: string; name: string }// Omit<T, K> - Exclude specific propertiestype UserWithoutEmail = Omit<User, "email">;// { id: string; name: string; age?: number }// Record<K, T> - Object with specific keys and value typetype RolePermissions = Record<"admin" | "user" | "guest", string[]>;// { admin: string[]; user: string[]; guest: string[] }// ReturnType<F> - Extract return type of functiontype FetchResult = ReturnType<typeof fetchUser>;// Promise<User | undefined>// Parameters<F> - Extract parameter typestype FetchParams = Parameters<typeof fetchUser>;// [userId: string]// Awaited<T> - Unwrap Promise typetype ResolvedUser = Awaited<ReturnType<typeof fetchUser>>;// User | undefined
Discriminated Unions
typescript
// Use a common "type" or "status" field as discriminatortype ApiResponse<T> =| { status: "success"; data: T }| { status: "error"; error: string }| { status: "loading" };function handleResponse(response: ApiResponse<User>) {switch (response.status) {case "success":console.log(response.data.name); // TypeScript knows data existsbreak;case "error":console.error(response.error); // TypeScript knows error existsbreak;case "loading":console.log("Loading...");break;}}// State machines with discriminated unionstype AuthState =| { state: "idle" }| { state: "loading" }| { state: "authenticated"; user: User }| { state: "error"; message: string };// Action types for reducerstype UserAction =| { type: "SET_USER"; payload: User }| { type: "CLEAR_USER" }| { type: "UPDATE_NAME"; payload: string };
Runtime Validation with Zod
typescript
import { z } from "zod";// Define schemaconst UserSchema = z.object({id: z.string().uuid(),name: z.string().min(1).max(100),email: z.string().email(),age: z.number().int().min(0).max(150).optional(),});// Infer TypeScript type from schematype User = z.infer<typeof UserSchema>;// Validate data (throws on error)const user = UserSchema.parse(untrustedData);// Safe validation (returns result object)const result = UserSchema.safeParse(untrustedData);if (result.success) {console.log(result.data); // User} else {console.error(result.error.issues);}// Common patternsconst ConfigSchema = z.object({apiUrl: z.string().url(),timeout: z.number().default(5000),retries: z.number().min(0).max(10).default(3),});// Transform and validateconst EmailSchema = z.string().email().transform((val) => val.toLowerCase());
Async/Await
typescript
// Async function with proper typingasync function fetchUser(userId: string): Promise<User | undefined> {const response = await fetch(`/api/users/${userId}`);if (!response.ok) return undefined;return response.json() as Promise<User>;}// Use Promise.all for concurrent operationsasync function fetchAllUsers(userIds: string[]): Promise<User[]> {const users = await Promise.all(userIds.map(fetchUser));return users.filter((user): user is User => user !== undefined);}// Handle errors with try/catchasync function safeFetch<T>(url: string): Promise<Result<T>> {try {const response = await fetch(url);const data = await response.json();return { success: true, data };} catch (error) {return { success: false, error: String(error) };}}
Error Handling
typescript
// Custom error classes for domain errorsclass UserNotFoundError extends Error {constructor(public readonly userId: string) {super(`User not found: ${userId}`);this.name = "UserNotFoundError";}}// Strict vs optional returnsfunction getUserStrict(userId: string): User {const user = repository.get(userId);if (!user) throw new UserNotFoundError(userId);return user;}function getUserOptional(userId: string): User | undefined {return repository.get(userId);}// Result type for explicit error handlingtype Result<T, E = Error> =| { ok: true; value: T }| { ok: false; error: E };
Comments - Less is More
typescript
// BAD - redundant comment// Get the user from databaseconst user = repository.getUser(userId);// GOOD - self-explanatory code, no comment neededconst user = repository.getUser(userId);// GOOD - comment explains WHY (not obvious)// Rate limit: API allows max 1000 requests/minawait rateLimiter.acquire();
Recommended Tooling
| Tool | Purpose | |
|---|---|---|
pnpm or bun | Package manager (faster than npm) | |
eslint | Linting with TypeScript rules | |
prettier | Code formatting | |
vitest or jest | Testing framework | |
tsx or ts-node | TypeScript execution | |
zod | Runtime validation with type inference |
tsconfig.json Recommendations
Note: These are strict settings for new projects. For existing codebases, enable incrementally.
json
{"compilerOptions": {"strict": true,"noUncheckedIndexedAccess": true,"noImplicitReturns": true,"noFallthroughCasesInSwitch": true,"exactOptionalPropertyTypes": true,"noPropertyAccessFromIndexSignature": true,"forceConsistentCasingInFileNames": true,"verbatimModuleSyntax": true}}
Production Best Practices
- Strict mode - Enable
strict: truein tsconfig.json - Explicit return types - Always declare return types for public functions
- Avoid any - Use
unknownand type guards instead - Readonly by default - Use
readonlyandas constfor immutable data - Discriminated unions - For state management and result types
- Dependency injection - Pass dependencies explicitly
- Custom errors - Domain-specific error classes
- Environment variables - Type-safe config with validation (zod, env-var)
- Barrel exports - Use index.ts for clean imports
- Path aliases - Configure
@/paths in tsconfig for cleaner imports
References
- Utility Types, Discriminated Unions, and Zod sections inspired by moai-lang-typescript by AJBcoding