TypeScript Best Practices
Guidelines for writing effective, maintainable, and type-safe TypeScript code.
Enable Strict Mode
Always enable strict mode in tsconfig.json:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}
Remember: Strict mode catches the most common bugs and ensures all types are explicit.
Avoid any
Use specific types, unknown, or generics instead:
// Bad
function process(data: any): any { return data; }
// Good
function process(data: { name: string; age: number }): string {
return `${data.name} is ${data.age}`;
}
// Better — unknown + type guard
function process(data: unknown): string {
if (typeof data === "string") return data;
return "Unknown";
}
Use Interfaces for Object Shapes
Interfaces support declaration merging, extending, and class implementation:
interface User {
name: string;
age: number;
}
interface Admin extends User {
role: string;
}
Tip: Prefer interfaces over type aliases for object shapes that might be extended.
Annotate Function Signatures
Always annotate parameters and return types to document your API:
function calculateTotal(items: { price: number }[], taxRate: number): number {
return items.reduce((sum, item) => sum + item.price, 0) * (1 + taxRate);
}
Use Discriminated Unions
Cleaner than complex if/else chains:
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rectangle"; width: number; height: number };
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle": return Math.PI * shape.radius ** 2;
case "rectangle": return shape.width * shape.height;
}
}
Use Partial for Optional Parameters
Partial makes all properties optional while keeping types safe:
interface Options {
timeout: number;
retries: number;
debug: boolean;
}
function configure(options: Partial<Options>): void {
const defaults: Options = { timeout: 5000, retries: 3, debug: false };
const settings = { ...defaults, ...options };
}
Best Practices
- Enable strict mode always
- Avoid
any— use specific types,unknown, or generics - Use interfaces for object shapes
- Annotate function signatures
- Use
as constfor literal types - Prefer discriminated unions over complex conditionals
- Use
Partialfor optional parameter objects - Generate declaration files for libraries
- Run the linter to enforce consistent style
Common Mistakes
- Using
anyexcessively defeats TypeScript’s purpose - Not enabling strict mode misses many type errors
- Overusing type assertions bypasses type safety
- Ignoring compiler warnings leads to hidden bugs
- Not using return type annotations misses type mismatches