================================================================================ TYPESCRIPT FUNDAMENTALS - CHALLENGE 2 SOLUTION Chapter 8: Functions & Overloads Challenge: Overload with Different Behavior ================================================================================ PROBLEM: Create a convertToJSON function that takes an object and optional formatting. If formatting is true, return pretty JSON. If false, return compact JSON. SOLUTION: ================================================================================ // Overload signatures - different behavior based on parameter function convertToJSON(obj: any, format: true): string; function convertToJSON(obj: any, format?: false): string; function convertToJSON(obj: any): string; // Implementation function convertToJSON(obj: any, format: boolean = false): string { if (format === true) { // Pretty print with 2-space indentation return JSON.stringify(obj, null, 2); } else { // Compact JSON with no extra whitespace return JSON.stringify(obj); } } // Testing the solution const user = { id: 1, name: "Alice", email: "alice@example.com", active: true }; // Pretty JSON (format = true) console.log("=== Pretty JSON ==="); console.log(convertToJSON(user, true)); // Compact JSON (format = false or omitted) console.log("\n=== Compact JSON ==="); console.log(convertToJSON(user, false)); console.log(convertToJSON(user)); ================================================================================ EXPECTED OUTPUT: ================================================================================ === Pretty JSON === { "id": 1, "name": "Alice", "email": "alice@example.com", "active": true } === Compact JSON === {"id":1,"name":"Alice","email":"alice@example.com","active":true} {"id":1,"name":"Alice","email":"alice@example.com","active":true} ================================================================================ WHY THIS WORKS: ================================================================================ 1. Multiple Overload Signatures - function convertToJSON(obj: any, format: true): string; → For when format is explicitly true - function convertToJSON(obj: any, format?: false): string; → For when format is false or omitted (default false) - function convertToJSON(obj: any): string; → For when no format parameter is provided 2. JSON.stringify Parameters - JSON.stringify(obj) → compact JSON - JSON.stringify(obj, null, 2) → pretty JSON with 2-space indent - The third parameter is the indentation level 3. Flexible Default Behavior - If format parameter is not provided, defaults to false (compact) - This is common for APIs - compact is usually the default 4. Self-Documenting Code - The overloads make it clear what the function can do - Callers know they can pass true for pretty printing ================================================================================ ALTERNATIVE APPROACHES: ================================================================================ Approach 1: Using a literal type union function convertToJSON(obj: any, format: "pretty" | "compact" = "compact"): string { if (format === "pretty") { return JSON.stringify(obj, null, 2); } else { return JSON.stringify(obj); } } // Callers use descriptive string constants convertToJSON(user, "pretty"); convertToJSON(user, "compact"); // More readable but longer signatures Approach 2: Using an options object interface JSONOptions { pretty?: boolean; indent?: number; } function convertToJSON(obj: any, options: JSONOptions = {}): string { const indent = options.pretty ? (options.indent ?? 2) : undefined; return JSON.stringify(obj, null, indent); } // More flexible for adding more options later convertToJSON(user, { pretty: true, indent: 4 }); Approach 3: Separate functions (no overloads needed) function toCompactJSON(obj: any): string { return JSON.stringify(obj); } function toPrettyJSON(obj: any, indent: number = 2): string { return JSON.stringify(obj, null, indent); } // Most explicit but requires multiple function names ================================================================================ ADVANCED VARIATION: WITH CUSTOM INDENT: ================================================================================ // More powerful version with custom indentation function convertToJSON(obj: any): string; function convertToJSON(obj: any, indent: number): string; function convertToJSON(obj: any, indent?: number): string { if (indent === undefined) { // No indent = compact return JSON.stringify(obj); } else if (indent === 0) { // Indent 0 = compact (edge case) return JSON.stringify(obj); } else { // Positive indent = pretty print with that indentation return JSON.stringify(obj, null, indent); } } // Usage console.log(convertToJSON(user)); // Compact console.log(convertToJSON(user, 2)); // Pretty with 2 spaces console.log(convertToJSON(user, 4)); // Pretty with 4 spaces console.log(convertToJSON(user, 0)); // Compact ================================================================================ REAL-WORLD EXAMPLES: ================================================================================ Example 1: Logging with Different Levels function log(message: any): void; function log(message: any, verbose: true): void; function log(message: any, verbose: boolean = false): void { if (verbose) { console.log("[VERBOSE]", JSON.stringify(message, null, 2)); } else { console.log("[INFO]", message); } } log("User logged in"); log({ user: "alice", timestamp: Date.now() }, true); Example 2: API Response Formatter function formatResponse(data: any, minify: boolean = false): string { if (minify) { return JSON.stringify(data); } else { return JSON.stringify(data, null, 2); } } // For APIs, minified is usually the default (smaller payload) function formatResponse(data: any, minify: boolean = true): string { // ... implementation } Example 3: Config File Writer function saveConfig(config: any, pretty: boolean = true): string { // Users usually want readable config files if (pretty) { return JSON.stringify(config, null, 2); } else { return JSON.stringify(config); } } // Save to file const configString = saveConfig({ debug: true, port: 3000 }); fs.writeFileSync("config.json", configString); ================================================================================ TESTING & VERIFICATION: ================================================================================ interface TestData { name: string; value: number; } const testObj: TestData = { name: "test", value: 42 }; // Test 1: Pretty format const pretty = convertToJSON(testObj, true); console.log("Test 1 (Pretty):"); console.log(pretty); console.log("Contains newlines:", pretty.includes("\n")); console.log("Matches regex:", /{\n\s+/.test(pretty)); // Test 2: Compact format (explicit false) const compact = convertToJSON(testObj, false); console.log("\nTest 2 (Compact - explicit false):"); console.log(compact); console.log("Contains newlines:", compact.includes("\n")); // Test 3: Compact format (default) const default_ = convertToJSON(testObj); console.log("\nTest 3 (Compact - default):"); console.log(default_); console.log("Compact and default are same:", compact === default_); // Test 4: Parse back and verify const parsed = JSON.parse(pretty); console.log("\nTest 4 (Round-trip):"); console.log("Parsed correctly:", parsed.name === "test" && parsed.value === 42); // Test 5: Nested objects const nested = { user: { id: 1, name: "Alice" }, posts: [ { id: 1, title: "Post 1" }, { id: 2, title: "Post 2" } ] }; console.log("\nTest 5 (Nested objects):"); console.log("Pretty:"); console.log(convertToJSON(nested, true)); console.log("\nCompact:"); console.log(convertToJSON(nested, false)); ================================================================================ KEY TAKEAWAYS: ================================================================================ ✅ Overloads can represent different behavior based on parameters ✅ JSON.stringify with null and indent number creates pretty JSON ✅ Default parameters make functions more convenient to use ✅ Boolean parameter with overloads is a common pattern ✅ Consider whether a boolean or options object is clearer ✅ Overloads help users understand what the function can do Pattern Summary: - format: true → pretty/verbose output - format: false/undefined → compact output - This pattern appears in many APIs (prettier, logger, etc.) ================================================================================