typescript-best-practices
TypeScript Best Practices
Quick Start
Always enable strict mode and use explicit types for public APIs. Prefer type-only imports (import type) and named exports over default exports. Use discriminated unions for state management and type guards for runtime validation.
Type Safety Fundamentals
Strict Mode Configuration
Enable all strict flags in tsconfig.json:
{
"strict": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"strictNullChecks": true,
"strictFunctionTypes": true
}
Null Safety
// Explicit null handling
export function findById<T>(items: Map<string, T>, id: string): T | null {
return items.get(id) ?? null;
}
// Optional chaining and nullish coalescing
export function getName(item?: Item): string {
return item?.name ?? 'Unknown';
}
Explicit Return Types
Use explicit return types for public APIs:
// Good
export function validateConfig(data: unknown): ValidatedConfig | null {
const result = ConfigSchema.safeParse(data);
return result.success ? result.data : null;
}
// Bad - implicit return type
export function validateConfig(data) {
return data;
}
Core Patterns
Discriminated Unions for State
export type ConnectionState =
| { status: 'disconnected' }
| { status: 'connecting'; progress: number }
| { status: 'connected'; connectionId: string }
| { status: 'error'; message: string };
// TypeScript knows which properties are available in each branch
Result Type for Error Handling
export type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
const result = await connectToService(options);
if (result.success) {
console.log('Connected:', result.data.id);
} else {
console.error('Failed:', result.error.message);
}
Readonly Properties
Use readonly for immutable data:
export interface Config {
readonly enableFeature: boolean;
readonly port: number;
}
Type Guards and Assertions
// Type guard
export function isUserData(data: unknown): data is UserData {
return typeof data === 'object' && data !== null && 'name' in data;
}
// Assertion function
export function assertIsDefined<T>(value: T): asserts value is NonNullable<T> {
if (value === undefined || value === null) throw new Error('Value is undefined or null');
}
Import/Export Conventions
// Type-only imports (preferred)
import type { Config } from '../types/config';
// Named exports (avoid default exports)
export class DataService {}
export const getService = () => DataService.getInstance();
// Grouped imports
import { External } from 'external'; // External deps
import { internalUtil } from './utils'; // Internal utils
import type { MyType } from './types'; // Types
When to Use References
| Reference File | When to Load |
|---|---|
tsconfig.md |
Setting up TypeScript configuration |
interfaces-types.md |
Defining interfaces, generic types, extending external types |
unions-discriminated.md |
Working with discriminated unions, result types, exhaustive checks |
type-guards-assertions.md |
Runtime validation, branded types, assertion functions |
utility-types.md |
Using built-in utilities, const assertions, template literal types |
error-handling.md |
Implementing structured errors, error factories, retry logic |
generics.md |
Building generic services, repositories, factories, event emitters |
import-export.md |
Organizing imports/exports, avoiding circular dependencies |
Common Anti-Patterns to Avoid
- Using
any- Useunknownwith type guards instead - Default exports - Harder to refactor and tree-shake
- Implicit return types on public APIs
- Non-readonly interfaces for immutable data
- Nested optionals (
{ a?: { b?: string } }) - use discriminated unions instead
More from ghosttypes/ff-5mp-api-ts
npm-to-pnpm
Migrate projects from npm to pnpm including lockfile conversion, workspace setup, CI/CD updates, and troubleshooting. Use when converting a single package from npm to pnpm, migrating npm workspaces or monorepos, updating CI/CD pipelines for pnpm, troubleshooting issues after npm to pnpm migration, or converting package-lock.json to pnpm-lock.yaml.
17sub-agent-creator
Interactive sub-agent creation skill for Claude Code. Use when user wants to create a custom subagent or mentions needing a specialized agent for specific tasks. This skill guides the entire subagent creation process including identifier design, system prompt generation, skill/context selection, and writing properly formatted agent files to .claude/agents.
12biome
Comprehensive Biome (biomejs.dev) integration for professional TypeScript/JavaScript development. Use for linting, formatting, code quality, and flawless Biome integration into codebases. Covers installation, configuration, migration from ESLint/Prettier, all linter rules, formatter options, CLI usage, editor integration, monorepo setup, and CI/CD integration. Use when working with Biome tooling, configuring biome.json, setting up linting/formatting, migrating projects, debugging Biome issues, or implementing production-ready Biome workflows.
11eslint
Professional-grade ESLint development for JavaScript and TypeScript projects. Use when working with ESLint for (1) configuring ESLint in projects, (2) understanding or fixing ESLint errors and warnings, (3) creating or modifying ESLint rules, (4) integrating ESLint into build systems or editors, (5) migrating ESLint configurations, (6) setting up custom parsers or plugins, (7) troubleshooting ESLint issues, (8) implementing code quality standards, or (9) any task involving ESLint setup, configuration, or usage. Covers all ESLint versions with focus on v9+ flat config format.
10vitest
Comprehensive Vitest testing framework skill for writing, configuring, and running tests in JavaScript/TypeScript projects. Use when Claude needs to: set up Vitest in a new or existing project (Vite or non-Vite), write or modify tests using Vitest APIs, configure Vitest for specific scenarios (coverage, browser testing, mocking, etc.), migrate from Jest or older Vitest versions, debug test failures or configuration issues, implement advanced testing patterns (workspace, browser mode, snapshots, mocking).
10github-actions
Comprehensive GitHub Actions workflow authoring skill. Enables Claude to write production-grade CI/CD workflows with latest best practices, syntax, and security patterns. Use when creating or modifying GitHub Actions workflows, designing CI/CD pipelines, configuring workflow triggers/events/schedules, implementing security patterns (secrets, OIDC, attestations), creating custom actions, migrating from other CI systems, troubleshooting failed workflows, or any GitHub Actions automation task. Includes complete reference documentation extracted from official GitHub docs with source URLs for verification.
10