TypeScript Best Practices for Production Code
TypeScript has become the standard for serious JavaScript development. Here are the practices that make the biggest difference in production codebases.
1. Prefer unknown Over any
any disables type checking entirely. unknown forces you to narrow the type before using it:
// Bad
function parse(input: any) {
return input.data; // No type safety
}
// Good
function parse(input: unknown) {
if (typeof input === 'object' && input !== null && 'data' in input) {
return input.data; // Type-safe
}
throw new Error('Invalid input');
}
2. Use Discriminated Unions
Instead of optional properties, use discriminated unions for clearer type narrowing:
// Instead of
type Result = {
success?: boolean;
data?: string;
error?: string;
};
// Use
type Result =
| { status: 'success'; data: string }
| { status: 'error'; error: string };
3. satisfies Operator
Use satisfies to validate a value matches a type while preserving the narrower inferred type:
const config = {
port: 3000,
host: 'localhost',
} satisfies Record<string, string | number>;
// config.port is still typed as number, not string | number
4. Template Literal Types
type Route = `/${string}`;
type ApiRoute = `/api/${string}`;
function navigate(route: Route) { /* ... */ }
navigate('/about'); // OK
navigate('about'); // Error
5. const Assertions
Use as const for literal types:
const COLORS = ['red', 'blue', 'green'] as const;
type Color = typeof COLORS[number]; // 'red' | 'blue' | 'green'
6. Strict Configuration
Always enable in tsconfig.json:
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
}
These practices catch entire categories of bugs at compile time rather than runtime.
Originally published on IceCat Studio Blog. Based on TypeScript documentation and production development patterns.