← TypeScript EnglishChapter 12 of 12

Best Practices

## Learning Objectives - Write clean, maintainable TypeScript - Follow naming conventions - Master type safety - Apply effective testing - Migrate from JavaScript ## Code Style ### Naming Conventions ```typescript // Classes: PascalCase class BankAccount { } // Interfaces: PascalCase (or sometimes I prefix) interface UserService { } interface IUser { } // Alternative: with I prefix // Variables and functions: camelCase let accountBalance: number; function calculateInterest() { } // Constants: UPPER_SNAKE_CASE const MAX_RETRY_COUNT = 3; const API_BASE_URL = "https://api.example.com"; // Type aliases and enums: PascalCase type UserRole = "admin" | "user"; enum HttpStatus { Ok, NotFound } ``` ### Formatting ```typescript // Use 2 or 4 spaces consistently // Line length: ~100 characters max // One declaration per line let age: number; let name: string; // Braces always on same line if (condition) { doSomething(); } else { doOther(); } ``` ## Type Safety ### Strict Mode ```json { "compilerOptions": { "strict": true, "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true } } ``` ### Avoid Any ```typescript // Bad function processData(data: any): any { return data; } // Good function processData(data: T): T { return data; } // If you must use any function processData(data: unknown): unknown { return data; } ``` ### Type Inference ```typescript // TypeScript infers: let x = 10 is number let x = 10; // x = "hello"; // Error // Explicit when needed let a: number | string; a = 10; a = "hello"; ``` ## Interfaces vs Type Aliases ### Use Interface for Objects ```typescript interface User { id: number; name: string; email: string; } ``` ### Use Type for Unions and Tuples ```typescript type ID = string | number; type Point = [number, number]; type Callback = () => void; ``` ## Functions ### Typed Parameters and Returns ```typescript // Good: explicit types function add(a: number, b: number): number { return a + b; } // Bad: relying on inference const add = (a, b) => a + b; ``` ### Optional Parameters ```typescript // Optional with ? function greet(name?: string): string { return name ? `Hello, ${name}!` : "Hello!"; } // Default values function createUser(name: string, role: string = "user") { return { name, role }; } ``` ## Classes ### Encapsulation ```typescript class BankAccount { private balance: number = 0; constructor(initialBalance: number = 0) { this.balance = initialBalance; } deposit(amount: number): void { if (amount > 0) { this.balance += amount; } } withdraw(amount: number): boolean { if (amount > 0 && amount <= this.balance) { this.balance -= amount; return true; } return false; } getBalance(): number { return this.balance; } } ``` ### readonly for Constants ```typescript class Config { readonly API_URL = "https://api.example.com"; readonly MAX_RETRIES = 3; } ``` ## Enums ### Use Const Enums ```typescript const enum Status { Active, Pending, Completed } if (status === Status.Active) { // ... } ``` ### String Enums for Flexibility ```typescript enum Direction { Up = "UP", Down = "DOWN", Left = "LEFT", Right = "RIGHT" } ``` ## Generics ### Constrain When Needed ```typescript // Generic with constraint function logLength(arg: T): T { console.log(arg.length); return arg; } logLength("hello"); // OK logLength([1, 2, 3]); // OK // logLength(123); // Error ``` ### Use Utility Types ```typescript // Don't reimplement type PartialUser = Partial; type UserPreview = Pick; type UserWithoutEmail = Omit; ``` ## Error Handling ### Specific Types ```typescript try { JSON.parse(invalidJson); } catch (error) { if (error instanceof SyntaxError) { console.error("JSON syntax error:", error.message); } else { throw error; } } ``` ### Result Pattern ```typescript type Result = | { success: true; value: T } | { success: false; error: E }; function divide(a: number, b: number): Result { if (b === 0) { return { success: false, error: new Error("Division by zero") }; } return { success: true, value: a / b }; } ``` ## Null Handling ### Optional Chaining ```typescript interface User { address?: { city: string; }; } let city = user?.address?.city; // undefined if missing ``` ### Nullish Coalescing ```typescript let name = userName ?? "Anonymous"; let count = items.length ?? 0; ``` ### Avoid Non-null Assertion ```typescript // Risky let name = value!; // Better if (value !== null && value !== undefined) { let name = value; } ``` ## Migration from JavaScript ### Step 1: Add tsconfig.json ```json { "compilerOptions": { "target": "ES2020", "module": "commonjs", "strict": false, "allowJs": true } } ``` ### Step 2: Enable checkJs ```json { "compilerOptions": { "checkJs": true } } ``` ### Step 3: Add Type Annotations ```javascript // Before (JavaScript) / function add(a, b) { return a + b; } ``` ```typescript // After (TypeScript) function add(a: number, b: number): number { return a + b; } ``` ### Step 4: Enable Strict Mode ```json { "compilerOptions": { "strict": true } } ``` ## Testing ### With Jest ```typescript // calculator.ts export function add(a: number, b: number): number { return a + b; } // calculator.test.ts import { add } from "./calculator"; test("adds two numbers", () => { expect(add(2, 3)).toBe(5); }); ``` ### Typed Mocks ```typescript interface UserService { getUser(id: number): Promise; } function getUserWithCache( id: number, service: UserService ): Promise { // Implementation } ``` ## Documentation ### JSDoc with Types ```typescript /** * Calculates the area of a rectangle. * * @param width - The width of the rectangle * @param height - The height of the rectangle * @returns The area (width * height) * @throws {Error} If width or height is negative */ function rectangleArea(width: number, height: number): number { if (width < 0 || height < 0) { throw new Error("Dimensions must be positive"); } return width * height; } ``` ## Tooling ### ESLint ```json { "parser": "@typescript-eslint/parser", "rules": { "no-unused-vars": "error", "prefer-const": "error" } } ``` ### Prettier ```json { "semi": true, "singleQuote": true, "trailingComma": "es5" } ``` ## Summary - Enable `strict: true` for type safety - Prefer interfaces for object shapes - Use type for unions, tuples, primitives - Avoid `any`, use `unknown` instead - Use optional chaining and nullish coalescing - Write pure functions when possible - Use `readonly` for constants - Document public APIs with JSDoc - Test typed functions - Migrate gradually from JavaScript

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →