diff --git a/frontend/JavaScript/readme.md b/frontend/JavaScript/readme.md deleted file mode 100644 index 7471a9c..0000000 --- a/frontend/JavaScript/readme.md +++ /dev/null @@ -1,801 +0,0 @@ ---- -description: Vibe coding guidelines and architectural constraints for JavaScript within the frontend domain. -technology: JavaScript -domain: frontend -level: Senior/Architect -version: ES6-ES2024 -tags: [javascript, clean-code, es6, performance, best-practices] -ai_role: Senior JavaScript Performance Expert -last_updated: 2026-03-22 ---- - -# JavaScript Best Practise - -![JavaScript Logo](https://img.icons8.com/?size=100&id=108784&format=png&color=000000) - -## I. Fundamentals (The Basics) - -## 1. `var` vs `const/let` -**Context:** Scoping and hoisting mechanisms in modern JavaScript. `var` is function-scoped and hoisted, leading to unpredictable behavior and accidental global leakage. -### ❌ Bad Practice -```javascript -var price = 100; -if (true) { - var price = 200; // Overwrites outer variable -} -console.log(price); // 200 -``` -### ⚠️ Problem -`var` does not respect block scope. Its hoisting behavior allows variables to be accessed before declaration (as `undefined`), which bypasses the Temporal Dead Zone (TDZ) safety mechanism, increasing cognitive load and bug density. -### ✅ Best Practice -```javascript -const price = 100; -if (true) { - const price = 200; // Block-scoped, unique to this block -} -console.log(price); // 100 -``` -### 🚀 Solution -Use `const` by default to ensure immutability of the reference. Use `let` only when reassigning a variable is strictly necessary. This enforces block-level scoping and prevents accidental overrides. - -## 2. Loose equality `==` -**Context:** JavaScript's type coercion rules are complex and often counter-intuitive. -### ❌ Bad Practice -```javascript -if (userCount == '0') { - // Executes if userCount is 0 (number) or '0' (string) -} -``` -### ⚠️ Problem -The Abstract Equality Comparison Algorithm (`==`) performs implicit type conversion. This leads to edge cases like `[] == ![]` being `true` or `0 == ''` being `true`, which can cause silent logic failures. -### ✅ Best Practice -```javascript -if (userCount === 0) { - // Strict comparison -} -``` -### 🚀 Solution -Always use strict equality `===` and inequality `!==`. This forces the developer to handle type conversions explicitly, making the code's intent clear and predictable. - -## 3. Global Scope Pollution -**Context:** The global namespace is shared. Overwriting global properties can break third-party libraries or browser APIs. -### ❌ Bad Practice -```javascript -// In a script file -const config = { api: '/v1' }; -function init() { /* ... */ } -``` -### ⚠️ Problem -Variables declared in the top-level scope of a non-module script are attached to `window` (in browsers) or `global` (in Node). This increases the risk of name collisions and memory leaks. -### ✅ Best Practice -```javascript -// use modules -export const config = { api: '/v1' }; - -// or IIFE if modules aren't available -(() => { - const config = { api: '/v1' }; -})(); -``` -### 🚀 Solution -Use ES Modules (`import/export`) to encapsulate code. Modules have their own scope and do not leak to the global object. - -## 4. String concatenation vs Template Literals -**Context:** Readability and handling of multi-line strings/expressions. -### ❌ Bad Practice -```javascript -const greeting = 'Hello, ' + user.firstName + ' ' + user.lastName + '! ' + - 'Welcome to ' + siteName + '.'; -``` -### ⚠️ Problem -Concatenation with `+` is error-prone, hard to read, and difficult to maintain for multi-line strings. It often leads to missing spaces and poor visual structure. -### ✅ Best Practice -```javascript -const greeting = `Hello, ${user.firstName} ${user.lastName}! -Welcome to ${siteName}.`; -``` -### 🚀 Solution -Use Template Literals (backticks). They allow for embedded expressions, multi-line strings, and superior readability. - -## 5. Magic Numbers -**Context:** Numbers with no context make the codebase hard to maintain. -### ❌ Bad Practice -```javascript -if (user.age >= 18) { - grantAccess(); -} -``` -### ⚠️ Problem -"18" is a magic number. If the legal age changes, you must find and replace every instance, risking errors if the same number is used for different contexts elsewhere. -### ✅ Best Practice -```javascript -const LEGAL_AGE = 18; - -if (user.age >= LEGAL_AGE) { - grantAccess(); -} -``` -### 🚀 Solution -Extract magic numbers into named constants. This provides semantic meaning and a single source of truth for configuration. - -## 6. Boolean comparisons `(if x === true)` -**Context:** Redundancy in conditional logic. -### ❌ Bad Practice -```javascript -if (isValid === true) { /* ... */ } -``` -### ⚠️ Problem -Comparing a boolean to `true` or `false` is redundant. It adds visual noise without increasing safety. -### ✅ Best Practice -```javascript -if (isValid) { /* ... */ } -if (!isPending) { /* ... */ } -``` -### 🚀 Solution -Leverage JavaScript's truthiness/falsiness or direct boolean evaluation. It makes the code more concise and idiomatic. - -## 7. Array/Object literal vs `new` constructor -**Context:** Object and Array instantiation. -### ❌ Bad Practice -```javascript -const list = new Array(1, 2, 3); -const map = new Object(); -``` -### ⚠️ Problem -The `Array` constructor is inconsistent: `new Array(3)` creates an empty array of length 3, while `new Array(3, 4)` creates `[3, 4]`. Literals are faster and more readable. -### ✅ Best Practice -```javascript -const list = [1, 2, 3]; -const map = {}; -``` -### 🚀 Solution -Use literals `[]` and `{}`. They are visually cleaner and perform slightly better as they don't involve a function call. - -## 8. Function length/complexity -**Context:** The Single Responsibility Principle (SRP). -### ❌ Bad Practice -```javascript -function processOrder(order) { - // 100 lines of validation, DB saving, email sending... -} -``` -### ⚠️ Problem -Large functions are hard to test, debug, and reuse. High cyclomatic complexity makes it difficult for the JIT compiler to optimize the function. -### ✅ Best Practice -```javascript -function validateOrder(order) { /* ... */ } -function saveToDatabase(order) { /* ... */ } -function notifyUser(order) { /* ... */ } - -function processOrder(order) { - validateOrder(order); - saveToDatabase(order); - notifyUser(order); -} -``` -### 🚀 Solution -Break functions into smaller, pure components. Aim for functions under 20 lines that do exactly one thing. - -## 9. Deeply nested `if/else` (Arrow code) -**Context:** Cognitive load and code readability. -### ❌ Bad Practice -```javascript -function getData(user) { - if (user) { - if (user.isActive) { - if (user.hasPermission) { - return fetchData(); - } - } - } -} -``` -### ⚠️ Problem -"Arrow code" (code that expands horizontally) is hard to follow. It forces the reader to keep track of multiple nesting levels in their mental stack. -### ✅ Best Practice -```javascript -function getData(user) { - if (!user || !user.isActive || !user.hasPermission) { - return null; - } - return fetchData(); -} -``` -### 🚀 Solution -Use "Guard Clauses" to return early. This flattens the structure and handles edge cases first, leaving the happy path at the lowest nesting level. - -## 10. Improper naming (Single letters) -**Context:** Self-documenting code. -### ❌ Bad Practice -```javascript -const d = new Date(); -const u = users.map(i => i.n); -``` -### ⚠️ Problem -Single-letter variables (except for standard loop indices like `i` or `j`) provide no context. They make the code unsearchable and confusing for other developers. -### ✅ Best Practice -```javascript -const today = new Date(); -const userNames = users.map(user => user.name); -``` -### 🚀 Solution -Use descriptive, camelCase names that convey the intent and data type of the variable. - ---- - -## II. Modern Syntax & FP (ES6-ES2024) - -## 11. Manual object property assignment vs Shorthand -**Context:** Reducing boilerplate in object creation. -### ❌ Bad Practice -```javascript -const name = 'Alice'; -const user = { - name: name, - age: age -}; -``` -### ⚠️ Problem -Redundant repetition of keys and values increases file size and makes the code noisier. -### ✅ Best Practice -```javascript -const name = 'Alice'; -const user = { name, age }; -``` -### 🚀 Solution -Use Property Shorthand. When the key and variable name match, omit the value. - -## 12. Using `arguments` vs Rest parameters -**Context:** Handling variable numbers of arguments. -### ❌ Bad Practice -```javascript -function sum() { - const args = Array.prototype.slice.call(arguments); - return args.reduce((a, b) => a + b); -} -``` -### ⚠️ Problem -The `arguments` object is not a real array (it lacks methods like `map` or `reduce`). It is also incompatible with arrow functions and optimization in some V8 versions. -### ✅ Best Practice -```javascript -const sum = (...args) => args.reduce((a, b) => a + b); -``` -### 🚀 Solution -Use Rest Parameters (`...args`). They create a real array and are more explicit about the function's intent. - -## 13. Manual array copying vs Spread -**Context:** Immutability and array manipulation. -### ❌ Bad Practice -```javascript -const original = [1, 2, 3]; -const copy = []; -for (let i = 0; i < original.length; i++) { - copy.push(original[i]); -} -``` -### ⚠️ Problem -Manual loops for copying are verbose and imperative. They increase the surface area for bugs (off-by-one errors). -### ✅ Best Practice -```javascript -const original = [1, 2, 3]; -const copy = [...original]; -``` -### 🚀 Solution -Use the Spread Operator (`...`). It is concise, declarative, and highly optimized by modern engines. - -## 14. Nested Destructuring -**Context:** Extracting data from complex objects. -### ❌ Bad Practice -```javascript -const city = user.location.address.city; -const zip = user.location.address.zip; -``` -### ⚠️ Problem -Repetitive property access is verbose and risks "cannot read property of undefined" errors if any parent object is missing. -### ✅ Best Practice -```javascript -const { location: { address: { city, zip } } } = user; -``` -### 🚀 Solution -Use nested destructuring to extract deeply nested values in a single statement. (Note: Combine with optional chaining if path existence isn't guaranteed). - -## 15. Default Parameters -**Context:** Handling missing arguments. -### ❌ Bad Practice -```javascript -function setRole(role) { - role = role || 'guest'; - // ... -} -``` -### ⚠️ Problem -Using `||` for defaults is dangerous if the argument is a "falsy" but valid value (like `0`, `false`, or `''`). -### ✅ Best Practice -```javascript -function setRole(role = 'guest') { - // ... -} -``` -### 🚀 Solution -Use ES6 Default Parameters. They only apply if the argument is `undefined`. - -## 16. `forEach` for data transformation vs `map/filter` -**Context:** Declarative vs Imperative programming. -### ❌ Bad Practice -```javascript -const double = []; -numbers.forEach(n => { - double.push(n * 2); -}); -``` -### ⚠️ Problem -`forEach` relies on side effects (mutating an outer array). It is less expressive and harder to chain than functional alternatives. -### ✅ Best Practice -```javascript -const double = numbers.map(n => n * 2); -``` -### 🚀 Solution -Use `map`, `filter`, and `reduce` for data transformations. They return new arrays and promote immutability. - -## 17. Object mutation vs Immutability -**Context:** State management and predictability. -### ❌ Bad Practice -```javascript -function updateAge(user) { - user.age = 30; // Mutates original object - return user; -} -``` -### ⚠️ Problem -Mutating objects passed by reference can lead to side effects in other parts of the application that share the same reference, making debugging a nightmare. -### ✅ Best Practice -```javascript -const updateAge = (user) => ({ ...user, age: 30 }); -``` -### 🚀 Solution -Treat objects as immutable. Use the spread operator to create copies with updated properties. - -## 18. Switch statements vs Object Literals -**Context:** Simplifying conditional branching. -### ❌ Bad Practice -```javascript -switch (action) { - case 'CREATE': return doCreate(); - case 'UPDATE': return doUpdate(); - default: return doNothing(); -} -``` -### ⚠️ Problem -`switch` statements are verbose, require `break` to prevent fallthrough bugs, and have a non-standard block scope. -### ✅ Best Practice -```javascript -const actions = { - CREATE: doCreate, - UPDATE: doUpdate -}; -return (actions[action] || doNothing)(); -``` -### 🚀 Solution -Use an Object Literal (or Map) as a lookup table. It is cleaner, faster, and more extensible. - -## 19. Not using Optional Chaining `?.` -**Context:** Safe property access in nested objects. -### ❌ Bad Practice -```javascript -const street = user && user.address && user.address.street; -``` -### ⚠️ Problem -The "logical AND" chain is verbose and repetitive. It quickly becomes unreadable with deeper nesting. -### ✅ Best Practice -```javascript -const street = user?.address?.street; -``` -### 🚀 Solution -Use Optional Chaining (`?.`). It short-circuits to `undefined` if any part of the chain is nullish. - -## 20. Not using Nullish Coalescing `??` -**Context:** Providing fallback values safely. -### ❌ Bad Practice -```javascript -const timeout = config.timeout || 5000; -``` -### ⚠️ Problem -If `config.timeout` is `0`, the code will incorrectly fall back to `5000` because `0` is falsy. -### ✅ Best Practice -```javascript -const timeout = config.timeout ?? 5000; -``` -### 🚀 Solution -Use Nullish Coalescing (`??`). It only falls back if the value is `null` or `undefined`, allowing `0`, `false`, and `''` to be valid. - ---- - -## III. Asynchronous & Logic - -## 21. Callback Hell vs Promises -**Context:** Managing asynchronous execution flow. -### ❌ Bad Practice -```javascript -getData(url, (err, res) => { - getDetails(res.id, (err, details) => { - saveData(details, (err, ok) => { - // Callback Hell - }); - }); -}); -``` -### ⚠️ Problem -Deeply nested callbacks (the "Pyramid of Doom") make error handling extremely difficult and code unreadable. -### ✅ Best Practice -```javascript -fetchData(url) - .then(res => fetchDetails(res.id)) - .then(details => saveData(details)) - .catch(err => handleError(err)); -``` -### 🚀 Solution -Use Promises to flatten the structure and centralize error handling with `.catch()`. - -## 22. Promise.then() nesting vs Async/Await -**Context:** Modern syntax for asynchronous code. -### ❌ Bad Practice -```javascript -function load() { - return api.get().then(res => { - return api.process(res).then(processed => { - return processed; - }); - }); -} -``` -### ⚠️ Problem -Even with Promises, `.then()` nesting can occur. It still feels like "callback style" logic. -### ✅ Best Practice -```javascript -async function load() { - const res = await api.get(); - const processed = await api.process(res); - return processed; -} -``` -### 🚀 Solution -Use `async/await`. It allows asynchronous code to be written and read like synchronous code, improving maintainability. - -## 23. Sequential `await` in loops vs `Promise.all` -**Context:** Parallelizing independent asynchronous operations. -### ❌ Bad Practice -```javascript -for (const id of ids) { - await fetchItem(id); // Pauses loop for each request -} -``` -### ⚠️ Problem -Sequential `await` in a loop causes a "waterfall" effect, where each request waits for the previous one to finish, significantly increasing total execution time. -### ✅ Best Practice -```javascript -const promises = ids.map(id => fetchItem(id)); -await Promise.all(promises); -``` -### 🚀 Solution -Use `Promise.all` to execute independent promises in parallel. This utilizes the full network/IO bandwidth. - -## 24. Missing `try/catch` in async -**Context:** Handling failures in async functions. -### ❌ Bad Practice -```javascript -async function getData() { - const data = await fetch(url); // If this fails, the process might crash - return data; -} -``` -### ⚠️ Problem -Unhandled exceptions in `async` functions result in unhandled promise rejections, which can lead to silent failures or process termination in Node.js. -### ✅ Best Practice -```javascript -async function getData() { - try { - const data = await fetch(url); - return data; - } catch (error) { - logError(error); - } -} -``` -### 🚀 Solution -Wrap `await` calls in `try/catch` blocks or use a higher-order function to catch errors. - -## 25. Floating point math errors (`0.1 + 0.2`) -**Context:** Precision issues in IEEE 754 arithmetic. -### ❌ Bad Practice -```javascript -if (0.1 + 0.2 === 0.3) { /* False! */ } -``` -### ⚠️ Problem -$\text{0.1} + \text{0.2} = \text{0.30000000000000004}$ due to binary representation limits. This leads to critical bugs in financial or scientific applications. -### ✅ Best Practice -```javascript -const EPSILON = Number.EPSILON; -const areEqual = (a, b) => Math.abs(a - b) < EPSILON; - -// Or for money: -const totalCents = (10 + 20); // 30 cents -``` -### 🚀 Solution -Use `Number.EPSILON` for comparisons or represent decimals as integers (e.g., cents instead of dollars) to avoid floating point drift. - -## 26. Multiple Boolean flags vs State Machine -**Context:** Managing complex component logic. -### ❌ Bad Practice -```javascript -const [isLoading, setIsLoading] = useState(false); -const [isError, setIsError] = useState(false); -const [isSuccess, setIsSuccess] = useState(false); -``` -### ⚠️ Problem -Multiple flags allow for "impossible states" (e.g., `isLoading` and `isError` both being `true`). This makes logic branches exponentially complex. -### ✅ Best Practice -```javascript -const [status, setStatus] = useState('IDLE'); // IDLE, LOADING, ERROR, SUCCESS -``` -### 🚀 Solution -Use a single state variable or a state machine. This ensures only one state is active at a time and simplifies transitions. - -## 27. Sync logic in Event Loop -**Context:** Keeping the UI responsive. -### ❌ Bad Practice -```javascript -function processLargeArray(arr) { - // Blocks the main thread for 2 seconds - arr.sort().forEach(item => complexCalc(item)); -} -``` -### ⚠️ Problem -JavaScript is single-threaded. Heavy synchronous computation blocks the Event Loop, causing the UI to freeze and preventing user interaction. -### ✅ Best Practice -```javascript -// Use Web Workers or break into chunks -function processInChunks(arr) { - if (arr.length === 0) return; - const chunk = arr.splice(0, 100); - process(chunk); - setTimeout(() => processInChunks(arr), 0); -} -``` -### 🚀 Solution -Offload heavy tasks to Web Workers or use `requestIdleCallback`/`setTimeout` to break long tasks into smaller chunks, allowing the browser to render between frames. - -## 28. Overusing `classes` where functions suffice -**Context:** Paradigm choice (OOP vs FP). -### ❌ Bad Practice -```javascript -class Calculator { - add(a, b) { return a + b; } -} -const calc = new Calculator(); -``` -### ⚠️ Problem -Classes introduce unnecessary overhead (prototype chain, `this` binding issues) and make tree-shaking harder for bundlers. -### ✅ Best Practice -```javascript -export const add = (a, b) => a + b; -``` -### 🚀 Solution -Use simple functions and modules for logic. Use classes only when you need to manage complex stateful instances with shared behavior. - -## 29. Hard-coded Error messages vs Error Classes -**Context:** Robust error handling and debugging. -### ❌ Bad Practice -```javascript -throw new Error('User not found'); -``` -### ⚠️ Problem -Parsing error messages in `catch` blocks is brittle. If the string changes, the error handling logic breaks. -### ✅ Best Practice -```javascript -class UserNotFoundError extends Error { - constructor(userId) { - super(`User ${userId} not found`); - this.name = 'UserNotFoundError'; - this.code = 404; - } -} -``` -### 🚀 Solution -Extend the `Error` class to create custom error types. Use `instanceof` check in catch blocks to handle specific errors differently. - -## 30. Unhandled Rejections -**Context:** Reliability of asynchronous flows. -### ❌ Bad Practice -```javascript -// No .catch() or try/catch -fetch('/api/data'); -``` -### ⚠️ Problem -Unhandled rejections create silent failures. In production environments, this can lead to memory leaks as the promise state stays pending or rejected without being cleared. -### ✅ Best Practice -```javascript -window.addEventListener('unhandledrejection', event => { - reportToSentry(event.reason); -}); -``` -### 🚀 Solution -Always handle promise rejections. Implement a global unhandled rejection listener as a safety net for monitoring. - ---- - -## IV. Professional & Niche (Senior Level) - -## 31. Memory Leaks: Unremoved Event Listeners -**Context:** Long-lived applications (SPAs). -### ❌ Bad Practice -```javascript -window.addEventListener('resize', () => this.handleResize()); -// Component unmounts, but listener remains -``` -### ⚠️ Problem -The event listener keeps a reference to the component/context, preventing garbage collection even after the component is destroyed. -### ✅ Best Practice -```javascript -const handler = () => this.handleResize(); -window.addEventListener('resize', handler); -// Cleanup: -window.removeEventListener('resize', handler); -``` -### 🚀 Solution -Always remove event listeners in cleanup phases (e.g., `componentWillUnmount` or `useEffect` return). Use `AbortController` for an even more modern approach to listener cleanup. - -## 32. Memory Leaks: Forgotten Intervals/Timeouts -**Context:** Managing temporal background tasks. -### ❌ Bad Practice -```javascript -setInterval(() => { - fetchStatus(); -}, 1000); -``` -### ⚠️ Problem -Intervals continue to run indefinitely until the page is closed, even if the data they process is no longer needed, consuming CPU and memory. -### ✅ Best Practice -```javascript -const intervalId = setInterval(fetchStatus, 1000); -// Later: -clearInterval(intervalId); -``` -### 🚀 Solution -Store the ID returned by `setTimeout` or `setInterval` and clear it when the task is no longer relevant. - -## 33. Closures inside loops (Memory/Scope issues) -**Context:** Understanding the Event Loop and closure capture. -### ❌ Bad Practice -```javascript -for (var i = 0; i < 5; i++) { - setTimeout(() => console.log(i), 100); // Prints '5' five times -} -``` -### ⚠️ Problem -`var` is function-scoped. By the time the `setTimeout` executes, the loop has finished and `i` is 5. Each closure shares the same reference to `i`. -### ✅ Best Practice -```javascript -for (let i = 0; i < 5; i++) { - setTimeout(() => console.log(i), 100); // Prints 0, 1, 2, 3, 4 -} -``` -### 🚀 Solution -Use `let` in loop headers. It creates a new binding for each iteration, ensuring the closure captures the value of `i` at that specific moment. - -## 34. Throwing Strings instead of `new Error()` -**Context:** Ensuring useful stack traces. -### ❌ Bad Practice -```javascript -throw 'Something went wrong'; -``` -### ⚠️ Problem -Throwing a string provides no stack trace. It makes it nearly impossible to determine where the error originated in a complex call stack. -### ✅ Best Practice -```javascript -throw new Error('Something went wrong'); -``` -### 🚀 Solution -Always throw an instance of `Error` (or a subclass). This captures the `stack` property, which is vital for debugging. - -## 35. Modifying Built-in Prototypes -**Context:** Ecosystem compatibility and stability. -### ❌ Bad Practice -```javascript -Array.prototype.last = function() { - return this[this.length - 1]; -}; -``` -### ⚠️ Problem -"Monkey patching" built-ins can lead to collisions if a future ECMAScript version implements a method with the same name but different behavior. It also breaks for-in loops if not handled carefully. -### ✅ Best Practice -```javascript -const last = (arr) => arr[arr.length - 1]; -``` -### 🚀 Solution -Use utility functions or wrapper classes instead of modifying global prototypes. - -## 36. Premature Optimization (e.g., bitwise for rounding) -**Context:** Readability vs Micro-benchmarks. -### ❌ Bad Practice -```javascript -const floor = ~~x; // Double bitwise NOT to floor -``` -### ⚠️ Problem -While `~~` is slightly faster in some engines, it makes the code cryptic. It also only works for numbers within the 32-bit integer range. -### ✅ Best Practice -```javascript -const floor = Math.floor(x); -``` -### 🚀 Solution -Prioritize readability. Modern JIT compilers are smart enough to optimize `Math.floor`. Only use bitwise tricks if profiling proves it's a critical bottleneck in a hot path. - -## 37. V8 Hidden Classes: Changing object shape after initialization -**Context:** V8 JIT optimization. -### ❌ Bad Practice -```javascript -function User(name) { - this.name = name; -} -const u1 = new User('Alice'); -u1.age = 25; // Dynamically adding property -``` -### ⚠️ Problem -V8 creates "Hidden Classes" to optimize object property access. Adding properties after initialization changes the "shape" of the object, causing V8 to drop to a slower "Dictionary Mode" for that object. -### ✅ Best Practice -```javascript -function User(name, age) { - this.name = name; - this.age = age; -} -const u1 = new User('Alice', 25); -``` -### 🚀 Solution -Initialize all object properties in the constructor or a factory function. Maintain a consistent object "shape" to keep V8 in the optimized path. - -## 38. Array Hole (Sparse Arrays) performance -**Context:** Memory allocation and JIT optimization. -### ❌ Bad Practice -```javascript -const arr = new Array(100); -arr[50] = 'val'; -``` -### ⚠️ Problem -Creating "holes" in arrays makes them "sparse". Sparse arrays are stored differently (as hash maps) which is much slower for iteration and access than "packed" arrays. -### ✅ Best Practice -```javascript -const arr = Array.from({ length: 100 }, () => null); -``` -### 🚀 Solution -Initialize arrays with default values (like `null` or `0`) instead of leaving empty slots. This keeps the array in "packed" mode. - -## 39. Using `eval()` or `new Function()` -**Context:** Security and performance. -### ❌ Bad Practice -```javascript -const result = eval('2 + 2'); -``` -### ⚠️ Problem -`eval()` executes strings as code, opening a massive XSS security vulnerability if the string contains user input. It also prevents the JIT compiler from optimizing the surrounding scope. -### ✅ Best Practice -```javascript -const result = new Function('a', 'b', 'return a + b')(2, 2); // Slightly better, but still risky -// Better: -const operations = { '+': (a, b) => a + b }; -``` -### 🚀 Solution -Avoid `eval()`. Use lookup tables, JSON parsing, or safe math libraries to handle dynamic logic. - -## 40. Micro-optimizations that hurt readability -**Context:** Maintaining a healthy codebase. -### ❌ Bad Practice -```javascript -for (let i = 0, len = arr.length; i < len; i++) { /* ... */ } -``` -### ⚠️ Problem -Caching `arr.length` was necessary 15 years ago. Today, modern engines optimize this automatically. Adding extra variables for micro-gains makes the code harder to read. -### ✅ Best Practice -```javascript -for (const item of arr) { /* ... */ } -``` -### 🚀 Solution -Focus on "Big O" complexity and clean code. Only micro-optimize after profiling identifies a specific performance hotspot. diff --git a/frontend/TypeScript/readme.md b/frontend/TypeScript/readme.md deleted file mode 100644 index ef6f948..0000000 --- a/frontend/TypeScript/readme.md +++ /dev/null @@ -1,785 +0,0 @@ ---- -description: Vibe coding guidelines and architectural constraints for TypeScript within the frontend domain. -technology: TypeScript -domain: frontend -level: Senior/Architect -version: "5.0+" -tags: [typescript, type-safety, clean-code, best-practices, architecture] -ai_role: Senior TypeScript Architecture Expert -last_updated: 2026-03-22 ---- - -# TypeScript Best Practise - -![TypeScript Logo](https://img.icons8.com/?size=100&id=uJM6fQYqDaZK&format=png&color=000000) - -## I. Fundamentals (1-10) - -## 1. `any` vs `unknown` -**Context:** Handling data of an uncertain type. `any` disables all type-checking, while `unknown` forces safety. -### ❌ Bad Practice -```typescript -function process(data: any) { - console.log(data.name); // No error, but might crash at runtime -} -``` -### ⚠️ Problem -`any` is a "get out of jail free" card that propagates through the codebase, effectively turning off TypeScript's benefits and hiding potential runtime exceptions. -### ✅ Best Practice -```typescript -function process(data: unknown) { - if (data && typeof data === 'object' && 'name' in data) { - console.log((data as { name: string }).name); - } -} -``` -### 🚀 Solution -Use `unknown` for values whose type is not yet determined. It requires a type check or assertion before usage, ensuring the developer acknowledges the data's structure. - -## 2. `null` vs `undefined` in APIs -**Context:** Distinguishing between "value not provided" and "value is empty." -### ❌ Bad Practice -```typescript -interface UserResponse { - bio: string | null | undefined; -} -``` -### ⚠️ Problem -Using both creates ambiguity. In JSON, `undefined` properties are often stripped, while `null` is preserved. Mixing them increases complexity in conditional checks. -### ✅ Best Practice -```typescript -interface UserResponse { - bio?: string | null; // Optional if missing, null if explicitly empty -} -``` -### 🚀 Solution -Standardize: use `undefined` (optional properties) for missing keys and `null` for intentional absence of value. Avoid using both for the same field unless strictly required by a legacy API. - -## 3. `Array` vs `T[]` -**Context:** Visual consistency in array declarations. -### ❌ Bad Practice -```typescript -const users: Array = []; -const complex: Array = []; -``` -### ⚠️ Problem -`Array` is more verbose and can be confused with other generic types. It is harder to scan in complex signatures. -### ✅ Best Practice -```typescript -const users: User[] = []; -const complex: (string | number)[] = []; -``` -### 🚀 Solution -Prefer the shorthand `T[]`. It is idiomatic, more readable, and clearly distinguishes arrays from other generic containers like `Record` or `Promise`. - -## 4. `interface` vs `type` -**Context:** Defining object structures and aliases. -### ❌ Bad Practice -```typescript -interface Point { x: number; y: number; } -interface ID extends string {} // Error: Interface can only extend objects -``` -### ⚠️ Problem -`interface` is limited to objects and allows "declaration merging," which can lead to accidental property overrides in global scopes. -### ✅ Best Practice -```typescript -type Point = { x: number; y: number; }; -type ID = string; -type Union = 'A' | 'B'; -``` -### 🚀 Solution -Use `type` for almost everything (unions, primitives, intersections). Use `interface` only when you specifically need declaration merging or for public library APIs where consumers might need to extend types. - -## 5. Function Overloads vs Union Types -**Context:** Handling functions with different input/output combinations. -### ❌ Bad Practice -```typescript -function format(input: string): string; -function format(input: number): string; -function format(input: any): string { - return String(input); -} -``` -### ⚠️ Problem -Overloads are verbose and can be harder to implement correctly. They often require `any` or complex type-casting in the implementation body. -### ✅ Best Practice -```typescript -function format(input: string | number): string { - return String(input); -} -``` -### 🚀 Solution -Prefer Union types when the implementation logic is identical for all types. Reserve overloads only for cases where the return type strictly depends on the input type and cannot be expressed via generics. - -## 6. Global Scope Pollution (Legacy Namespaces) -**Context:** Organizing code in the ES Module era. -### ❌ Bad Practice -```typescript -namespace Utils { - export const log = (msg: string) => console.log(msg); -} -``` -### ⚠️ Problem -Namespaces are a legacy TypeScript feature. They don't play well with modern bundlers (Tree Shaking), are harder to test, and can lead to naming collisions in the global scope. -### ✅ Best Practice -```typescript -// utils.ts -export const log = (msg: string) => console.log(msg); -``` -### 🚀 Solution -Use ES Modules (`export`/`import`). They are the industry standard, supported by all modern environments, and allow for better static analysis. - -## 7. `enum` vs `const object` -**Context:** Grouping related constants. -### ❌ Bad Practice -```typescript -enum Status { - Active, - Inactive -} -``` -### ⚠️ Problem -Enums generate extra runtime code and have "reverse mapping" behavior that can lead to bugs (e.g., `Status[0]` returns "Active"). They also don't align with "TypeScript as a type-only layer." -### ✅ Best Practice -```typescript -const STATUS = { - ACTIVE: 'active', - INACTIVE: 'inactive' -} as const; - -type Status = typeof STATUS[keyof typeof STATUS]; -``` -### 🚀 Solution -Use `const` objects with `as const` and a derived union type. This is more predictable, emits cleaner code, and is easier to iterate over. - -## 8. Explicit `any` in Parameters -**Context:** Enforcing strict type safety. -### ❌ Bad Practice -```typescript -function save(data) { // Implicit any if strict: false - db.push(data); -} -``` -### ⚠️ Problem -Implicit `any` bypasses the compiler's ability to verify data flow, leading to "undefined is not a function" errors that TypeScript was designed to prevent. -### ✅ Best Practice -```typescript -function save(data: UserData) { - db.push(data); -} -``` -### 🚀 Solution -Enable `noImplicitAny: true` in `tsconfig.json`. Always define specific types or use `unknown` if the type is truly dynamic. - -## 9. Manual Type Guards vs Type Predicates -**Context:** Narrowing types inside conditional blocks. -### ❌ Bad Practice -```typescript -if (typeof input === 'object' && input !== null && 'admin' in input) { - const isAdmin = (input as any).admin; -} -``` -### ⚠️ Problem -Repeating complex checks is error-prone and requires manual casting (`as any`) which breaks safety. -### ✅ Best Practice -```typescript -function isAdmin(user: unknown): user is Admin { - return !!user && typeof user === 'object' && 'admin' in user; -} - -if (isAdmin(input)) { - console.log(input.admin); // input is automatically narrowed to Admin -} -``` -### 🚀 Solution -Use Type Predicates (`arg is Type`) to create reusable, safe narrowing functions. - -## 10. Triple-Slash Directives -**Context:** Referencing types or files. -### ❌ Bad Practice -```typescript -/// -``` -### ⚠️ Problem -Triple-slash directives are legacy syntax. They make dependencies implicit and can lead to compilation order issues. -### ✅ Best Practice -```typescript -import { MyType } from './types'; -``` -### 🚀 Solution -Use standard ES `import` statements. Manage global types via `tsconfig.json` `types` array if necessary. - ---- - -## II. Logic & Safety (11-20) - -## 11. Type Assertions (`as`) vs Narrowing -**Context:** Telling the compiler what a type is. -### ❌ Bad Practice -```typescript -const user = response.data as User; -console.log(user.id); -``` -### ⚠️ Problem -`as` forces the compiler to trust you. If the runtime data doesn't match the interface, the app will crash silently. -### ✅ Best Practice -```typescript -const user = UserSchema.parse(response.data); // Using Zod for runtime validation -// OR -if (isValidUser(response.data)) { ... } -``` -### 🚀 Solution -Avoid type assertions. Use runtime validation (Zod, Valibot) or Type Guards to ensure the data actually matches the type you expect. - -## 12. Non-null Assertion Operator (`!`) -**Context:** Dealing with potentially `null` or `undefined` values. -### ❌ Bad Practice -```typescript -const name = user!.profile!.name; -``` -### ⚠️ Problem -The `!` operator suppresses the compiler warning but doesn't handle the runtime reality. If `user` is null, this throws a `TypeError`. -### ✅ Best Practice -```typescript -const name = user?.profile?.name ?? 'Guest'; -``` -### 🚀 Solution -Use Optional Chaining (`?.`) and Nullish Coalescing (`??`) to handle missing values gracefully. - -## 13. Lack of Discriminated Unions -**Context:** Modeling complex states like API responses. -### ❌ Bad Practice -```typescript -interface State { - isLoading: boolean; - data?: string; - error?: string; -} -``` -### ⚠️ Problem -This allows "impossible states" (e.g., `isLoading: true` AND `data: '...'`). It requires awkward optional checking. -### ✅ Best Practice -```typescript -type State = - | { type: 'LOADING' } - | { type: 'SUCCESS', data: string } - | { type: 'ERROR', error: string }; -``` -### 🚀 Solution -Use Discriminated Unions (with a shared literal property like `type` or `kind`). This makes states mutually exclusive and simplifies logic. - -## 14. Boolean casting (`!!`) -**Context:** Converting values to booleans. -### ❌ Bad Practice -```typescript -const hasAccess = !!user.token; -``` -### ⚠️ Problem -`!!` is cryptic and less readable for beginners. It also doesn't provide type safety if the underlying value could be complex. -### ✅ Best Practice -```typescript -const hasAccess = Boolean(user.token); -// OR -const hasAccess = user.token !== undefined; -``` -### 🚀 Solution -Use the `Boolean()` constructor or explicit comparisons for clarity. - -## 15. Using `Object` for non-primitive types -**Context:** Restricting types to objects. -### ❌ Bad Practice -```typescript -function cache(obj: Object) { ... } -``` -### ⚠️ Problem -The `Object` type (capital O) includes primitives like `string` or `number` because they have methods. `object` (lowercase) is also vague. -### ✅ Best Practice -```typescript -function cache(obj: Record) { ... } -``` -### 🚀 Solution -Use `Record` for general objects or `Record` for empty objects to ensure keys are strings and values are handled safely. - -## 16. Function types vs Object types for functions -**Context:** Defining function signatures. -### ❌ Bad Practice -```typescript -type ClickHandler = { - (e: Event): void; -}; -``` -### ⚠️ Problem -Using the object literal syntax for single functions is unnecessarily complex and harder to read. -### ✅ Best Practice -```typescript -type ClickHandler = (e: Event) => void; -``` -### 🚀 Solution -Use the arrow function syntax for type aliases unless you need to define properties on the function itself (callable objects). - -## 17. Catching `any` in try-catch -**Context:** Handling exceptions. -### ❌ Bad Practice -```typescript -try { - doWork(); -} catch (e) { - console.error(e.message); // e is any by default -} -``` -### ⚠️ Problem -In JavaScript, anything can be thrown (`throw "error"`). Accessing `.message` on a string or null will crash. -### ✅ Best Practice -```typescript -try { - doWork(); -} catch (e) { - if (e instanceof Error) { - console.error(e.message); - } -} -``` -### 🚀 Solution -Always check the type of the caught error. In modern TS, use `useUnknownInCatchVariables: true` to force `e` to be `unknown`. - -## 18. Literal types vs General types -**Context:** Narrowing strings/numbers to specific values. -### ❌ Bad Practice -```typescript -function setAlignment(dir: string) { ... } -``` -### ⚠️ Problem -Accepting any `string` allows invalid inputs like `"center-left"` which the code won't handle. -### ✅ Best Practice -```typescript -type Direction = 'left' | 'right' | 'center'; -function setAlignment(dir: Direction) { ... } -``` -### 🚀 Solution -Use Union Literal types to restrict inputs to a known set of valid values. - -## 19. Optional properties vs Union with `undefined` -**Context:** Defining fields that might not exist. -### ❌ Bad Practice -```typescript -interface Config { - port: number | undefined; -} -``` -### ⚠️ Problem -This requires the key `port` to be present, even if its value is `undefined`. -### ✅ Best Practice -```typescript -interface Config { - port?: number; -} -``` -### 🚀 Solution -Use `?` for properties that can be omitted entirely. - -## 20. Array index access safety -**Context:** Accessing elements by index. -### ❌ Bad Practice -```typescript -const first = users[0]; -console.log(first.id); // Potential crash if array is empty -``` -### ⚠️ Problem -TypeScript assumes `users[0]` always exists if the array type is `User[]`. -### ✅ Best Practice -```typescript -const first = users[0]; -if (first) { - console.log(first.id); -} -``` -### 🚀 Solution -Enable `noUncheckedIndexedAccess: true` in `tsconfig.json`. This forces index access to return `T | undefined`. - ---- - -## III. Objects & Functions (21-30) - -## 21. Object literals vs `Record` -**Context:** Defining maps/dictionaries. -### ❌ Bad Practice -```typescript -const prices: { [key: string]: number } = { apple: 1 }; -``` -### ⚠️ Problem -The index signature syntax is more verbose and harder to read. -### ✅ Best Practice -```typescript -const prices: Record = { apple: 1 }; -``` -### 🚀 Solution -Use the `Record` utility type for key-value maps. - -## 22. Excess property checks and object spreading -**Context:** Passing objects to functions. -### ❌ Bad Practice -```typescript -const extra = { id: 1, name: 'A', extra: true }; -saveUser({ id: 1, name: 'A', extra: true }); // Error: excess property -saveUser(extra); // No error, but 'extra' is leaked into db -``` -### ⚠️ Problem -Excess property checks only happen on object literals. Spreading or passing variables can bypass this, leading to data pollution. -### ✅ Best Practice -```typescript -const { extra, ...validUser } = data; -saveUser(validUser); -``` -### 🚀 Solution -Be explicit about what data you pass. Use destructuring to strip unknown properties before passing objects to storage or APIs. - -## 23. `Readonly` for Immutability -**Context:** Preventing accidental state mutation. -### ❌ Bad Practice -```typescript -function process(config: Config) { - config.port = 80; // Side effect! -} -``` -### ⚠️ Problem -Mutable inputs lead to unpredictable state changes and bugs that are hard to trace in large applications. -### ✅ Best Practice -```typescript -function process(config: Readonly) { - // config.port = 80; // Error -} -``` -### 🚀 Solution -Use `Readonly` for function parameters and `as const` for configuration objects to enforce immutability at compile time. - -## 24. `Awaited` for Promise Unwrapping -**Context:** Getting the resolved type of a Promise. -### ❌ Bad Practice -```typescript -type Result = typeof apiCall extends Promise ? U : never; -``` -### ⚠️ Problem -Manually unwrapping promises via conditional types is complex and doesn't handle nested promises. -### ✅ Best Practice -```typescript -type Result = Awaited>; -``` -### 🚀 Solution -Use the `Awaited` utility type (TS 4.5+) for clean promise unwrapping. - -## 25. `this` typing in functions -**Context:** Ensuring correct context in callback-heavy code. -### ❌ Bad Practice -```typescript -function handleClick(this: any, event: Event) { - this.classList.add('active'); -} -``` -### ⚠️ Problem -`this` defaults to `any`, making it easy to access properties that don't exist on the actual execution context. -### ✅ Best Practice -```typescript -function handleClick(this: HTMLElement, event: Event) { - this.classList.add('active'); -} -``` -### 🚀 Solution -Always type the first "fake" `this` parameter in functions that rely on a specific execution context. - -## 26. Constructor Shorthand -**Context:** Defining class properties. -### ❌ Bad Practice -```typescript -class User { - public name: string; - constructor(name: string) { - this.name = name; - } -} -``` -### ⚠️ Problem -Redundant repetition of property names in declaration, parameter, and assignment. -### ✅ Best Practice -```typescript -class User { - constructor(public readonly name: string) {} -} -``` -### 🚀 Solution -Use parameter properties in constructors to declare and initialize class members in one step. - -## 27. Abstract classes vs Interfaces -**Context:** Defining blueprints for classes. -### ❌ Bad Practice -```typescript -class BaseService { - getData() { throw new Error("Not implemented"); } -} -``` -### ⚠️ Problem -Normal classes don't force implementation, leading to runtime errors. Interfaces don't allow shared logic. -### ✅ Best Practice -```typescript -abstract class BaseService { - abstract getData(): Promise; - log(msg: string) { console.log(msg); } -} -``` -### 🚀 Solution -Use `abstract` classes when you need to provide shared logic while forcing sub-classes to implement specific methods. - -## 28. Private vs `#private` -**Context:** Encapsulating data in classes. -### ❌ Bad Practice -```typescript -class User { - private secret = 123; -} -console.log(user['secret']); // Works at runtime -``` -### ⚠️ Problem -TypeScript's `private` keyword is only for compile-time. At runtime, the property is fully accessible. -### ✅ Best Practice -```typescript -class User { - #secret = 123; -} -``` -### 🚀 Solution -Use ES2020 `#private` fields for true runtime encapsulation if you are building libraries or high-security components. - -## 29. Decorators (Legacy vs TC39) -**Context:** Meta-programming in TypeScript. -### ❌ Bad Practice -```typescript -// Using experimentalDecorators: true -@Logged -class MyClass {} -``` -### ⚠️ Problem -Legacy decorators are non-standard and might break in future versions of Node/Browsers. -### ✅ Best Practice -Use the new TC39 Decorators (TS 5.0+) which align with the official JavaScript proposal. -### 🚀 Solution -If starting a new project, avoid decorators unless using a framework that mandates them (like NestJS or Angular). - -## 30. Utility Types (`Omit`, `Pick`, `Partial`) -**Context:** Transforming existing types. -### ❌ Bad Practice -```typescript -interface UserUpdate { - name?: string; - age?: number; -} -``` -### ⚠️ Problem -Manual re-declaration of properties leads to synchronization issues when the base `User` interface changes. -### ✅ Best Practice -```typescript -type UserUpdate = Partial>; -``` -### 🚀 Solution -Always derive sub-types from the source of truth using built-in utility types. - ---- - -## IV. Professional & Niche (31-40) - -## 31. Using `satisfies` to preserve literal types -**Context:** Checking an object against a type without widening it. -### ❌ Bad Practice -```typescript -const config: Record = { - host: 'localhost' -}; -// config.host is type 'string', not 'localhost' -``` -### ⚠️ Problem -Direct type annotation widens properties to the most general type, losing specific literal information needed for inference. -### ✅ Best Practice -```typescript -const config = { - host: 'localhost' -} satisfies Record; -// config.host is type 'localhost' -``` -### 🚀 Solution -Use `satisfies` (TS 4.9+). It validates the structure but preserves the narrowest possible type for the value. - -## 32. `const` type parameters (TS 5.0) -**Context:** Improving inference for generic constants. -### ❌ Bad Practice -```typescript -function route(paths: T) { ... } -route(['/home', '/about']); // T is string[] -``` -### ⚠️ Problem -Generic inference often widens string arrays to `string[]` unless the caller adds `as const`. -### ✅ Best Practice -```typescript -function route(paths: T) { ... } -route(['/home', '/about']); // T is readonly ['/home', '/about'] -``` -### 🚀 Solution -Use `const` before a type parameter to force the compiler to treat the input as a constant, preserving literal types without requiring the caller to use `as const`. - -## 33. Branding/Tagging for Nominal Typing -**Context:** Preventing accidental mixing of identical primitive types (e.g., `UserId` and `OrderId`). -### ❌ Bad Practice -```typescript -type UserId = string; -type OrderId = string; - -const ship = (u: UserId, o: OrderId) => {}; -ship('order_123', 'user_456'); // No error, but logic is wrong! -``` -### ⚠️ Problem -TypeScript is structural. Two type aliases of `string` are identical and interchangeable. -### ✅ Best Practice -```typescript -type Brand = K & { __brand: T }; -type UserId = Brand; -type OrderId = Brand; - -// Usage requires a cast at creation, but provides safety after -const uid = 'user_1' as UserId; -``` -### 🚀 Solution -Use "Branding" (adding a phantom property) to simulate nominal typing for critical identifiers. - -## 34. Covariance/Contravariance in callbacks -**Context:** Ensuring safe function assignments. -### ❌ Bad Practice -```typescript -interface Logger { - log: (msg: string) => void; // Defined as a property -} -``` -### ⚠️ Problem -Function properties are checked **bivariantly**, which is less strict and can lead to runtime errors when assigning functions with more specialized arguments. -### ✅ Best Practice -```typescript -interface Logger { - log(msg: string): void; // Defined as a method -} -``` -### 🚀 Solution -Use method syntax in interfaces for stricter **contravariant** checking of parameters. - -## 35. Avoiding "God Objects" through Mapped Types -**Context:** Transforming object structures dynamically. -### ❌ Bad Practice -```typescript -interface API { - getUser: () => void; - getPost: () => void; -} -``` -### ⚠️ Problem -Manually adding properties to large interfaces is repetitive and error-prone. -### ✅ Best Practice -```typescript -type Resource = 'User' | 'Post'; -type API = { - [K in Resource as `get${K}`]: () => void; -}; -``` -### 🚀 Solution -Use Mapped Types and Key Remapping (`as`) to generate interface structures from a single source of truth (like a union of keys). - -## 36. Template Literal Types for string-based APIs -**Context:** Enforcing patterns in strings. -### ❌ Bad Practice -```typescript -function setPadding(value: string) { ... } // Accepts "10" (invalid) -``` -### ⚠️ Problem -Strings used for CSS or IDs often have strict patterns that `string` doesn't capture. -### ✅ Best Practice -```typescript -type CssValue = `${number}${'px' | 'em' | 'rem'}`; -function setPadding(value: CssValue) { ... } -``` -### 🚀 Solution -Use Template Literal types to enforce specific string patterns at compile time. - -## 37. Exhaustiveness checking with `never` -**Context:** Ensuring all cases in a union are handled. -### ❌ Bad Practice -```typescript -function handle(action: 'START' | 'STOP') { - switch (action) { - case 'START': return 1; - case 'STOP': return 0; - } -} -``` -### ⚠️ Problem -If you add `'PAUSE'` to the union, the compiler won't warn you that `handle` is now missing a case. -### ✅ Best Practice -```typescript -function handle(action: 'START' | 'STOP' | 'PAUSE') { - switch (action) { - case 'START': return 1; - case 'STOP': return 0; - default: { - const _exhaustive: never = action; // Error: 'PAUSE' is not assignable to never - return _exhaustive; - } - } -} -``` -### 🚀 Solution -Assign the `default` case to a variable of type `never`. This triggers a compile error if any member of the union is unhandled. - -## 38. Recursive Type Aliases -**Context:** Modeling nested structures like JSON or file trees. -### ❌ Bad Practice -```typescript -type Json = string | number | boolean | JsonObject | JsonArray; -interface JsonObject { [key: string]: Json; } -``` -### ⚠️ Problem -Older TS versions required interfaces for recursion. Modern TS allows direct recursion. -### ✅ Best Practice -```typescript -type JSONValue = - | string | number | boolean | null - | { [key: string]: JSONValue } - | JSONValue[]; -``` -### 🚀 Solution -Use recursive type aliases for cleaner definitions of deeply nested data structures. - -## 39. `infer` keyword in conditional types -**Context:** Extracting internal types from complex structures. -### ❌ Bad Practice -```typescript -// Hardcoded extraction -type GetArrayType = T extends string[] ? string : never; -``` -### ⚠️ Problem -Hardcoding extractions limits reusability and doesn't scale. -### ✅ Best Practice -```typescript -type GetArrayType = T extends (infer U)[] ? U : never; -``` -### 🚀 Solution -Use `infer` within conditional types to let TypeScript dynamically capture and name types from within generics. - -## 40. Tuple types for fixed-length data -**Context:** Representing arrays with specific structures (e.g., coordinates). -### ❌ Bad Practice -```typescript -const point: number[] = [10, 20]; -const [x, y, z] = point; // z is undefined, but TS doesn't know -``` -### ⚠️ Problem -`number[]` doesn't capture the length, leading to potential out-of-bounds errors during destructuring. -### ✅ Best Practice -```typescript -const point: [number, number] = [10, 20]; -// const [x, y, z] = point; // Error: Tuple has 2 elements -``` -### 🚀 Solution -Use Tuples for arrays where the number of elements and their positions are fixed and meaningful. diff --git a/frontend/angular/advanced-performance.md b/frontend/angular/advanced-performance.md new file mode 100644 index 0000000..0eb5efa --- /dev/null +++ b/frontend/angular/advanced-performance.md @@ -0,0 +1,216 @@ +--- +description: Vibe coding guidelines and architectural constraints for Angular Advanced Performance within the frontend domain. +technology: Angular +domain: frontend +level: Senior/Architect +version: "20" +tags: [performance, advanced, angular, best-practices, clean-code, scalable-code] +ai_role: Senior Angular Performance Expert +last_updated: 2026-03-22 +--- + +# 🚀 Angular Advanced Performance Best Practices & Expert Patterns + +# 📖 Context & Scope +- **Primary Goal:** Enforce strict adherence to advanced performance best practices. +- **Target Tooling:** Cursor, Windsurf, Antigravity. +- **Tech Stack Version:** Angular 20 + +## III. Advanced Performance (31-45) + +## 31. Eager Loading of Heavy Components +**Context:** Bundle Size +### ❌ Bad Practice +```html + +``` +### ⚠️ Problem +A charting library (e.g., ECharts) loads immediately, blocking TTI (Time to Interactive), even if the chart is below the "fold". +### ✅ Best Practice +```html +@defer (on viewport) { + +} @placeholder { +
Loading chart...
+} +``` +### 🚀 Solution +Use `@defer`. This defers component code loading until a trigger occurs (viewport, interaction, timer). + +## 32. Heavy Computation in Main Thread +**Context:** Event Loop Blocking +### ❌ Bad Practice +Sorting an array of 100k elements directly in the component. +### ⚠️ Problem +Freezes the UI. +### ✅ Best Practice +Offload computations to a Web Worker. +### 🚀 Solution +Use Angular Web Workers. In v20, this is easily configured via the CLI. + +## 33. Memory Leaks in `effect()` +**Context:** Signal Effects +### ❌ Bad Practice +```typescript +effect(() => { + const timer = setInterval(() => ..., 1000); + // No cleanup +}); +``` +### ⚠️ Problem +Effects restart when dependencies change. If you don't clean up timers/subscriptions inside an effect, they accumulate. +### ✅ Best Practice +```typescript +effect((onCleanup) => { + const timer = setInterval(() => ..., 1000); + onCleanup(() => clearInterval(timer)); +}); +``` +### 🚀 Solution +Always use the `onCleanup` callback to release resources. + +## 34. Excessive Change Detection with `NgZone.run()` +**Context:** Zone Integration +### ❌ Bad Practice +Wrapping third-party libraries in `ngZone.run()` unnecessarily. +### ⚠️ Problem +Forces redundant checks of the entire component tree. +### ✅ Best Practice +```typescript +ngZone.runOutsideAngular(() => { + // Heavy chart rendering or canvas animation +}); +``` +### 🚀 Solution +Run frequent events (scroll, mousemove, animationFrame) *outside* the Angular zone. Update signals only when UI updates are required. + +## 35. Signals equality check default +**Context:** Signal Performance +### ❌ Bad Practice +```typescript +data = signal({ id: 1 }, { equal: undefined }); // Default checks reference +``` +### ⚠️ Problem +If you create a new object with the same data `{ id: 1 }`, the signal triggers an update, even though the data hasn't fundamentally changed. +### ✅ Best Practice +```typescript +import { isEqual } from 'lodash-es'; +data = signal(obj, { equal: isEqual }); +``` +### 🚀 Solution +Use a custom comparison function for complex objects to avoid redundant re-renders. + +## 36. Lacking `trackBy` in iterables +**Context:** Re-rendering Lists +### ❌ Bad Practice +```html +
  • {{ item }}
  • +``` +### ⚠️ Problem +Without tracking, any array change leads to the recreation of all DOM nodes in the list. $O(n)$ DOM operations. +### ✅ Best Practice +```html +@for (item of items; track item.id) +``` +### 🚀 Solution +Always use a unique key in `track`. This allows Angular to move DOM nodes instead of recreating them. + +## 37. Recursive Template without Caching +**Context:** Tree Rendering +### ❌ Bad Practice +Recursive component call without `OnPush` and memoization. +### ⚠️ Problem +Exponential growth in change detection checks. +### ✅ Best Practice +Using the `Memoization` pattern or `computed()` to prepare the tree data structure. + +## 38. Global Styles Leakage +**Context:** CSS Encapsulation +### ❌ Bad Practice +```css +/* global.css */ +button { padding: 10px; } +``` +### ⚠️ Problem +Global styles unpredictably affect components. +### ✅ Best Practice +Use `ViewEncapsulation.Emulated` (default) and specific selectors. +### 🚀 Solution +Keep styles locally within components. + +## 39. Large Component Bundle +**Context:** Split Chunks +### ❌ Bad Practice +A single huge component of 3000 lines. +### ⚠️ Problem +Poor readability, rendering lazy loading of UI parts impossible. +### ✅ Best Practice +Decompose into "dumb" (UI) and "smart" components. +### 🚀 Solution +Break down the UI into small, reusable blocks. + +## 40. Image Optimization Ignorance +**Context:** Core Web Vitals (LCP) +### ❌ Bad Practice +```html + +``` +### ⚠️ Problem +The browser loads the full image, shifting the layout (CLS). +### ✅ Best Practice +```html + +``` +### 🚀 Solution +Use the `NgOptimizedImage` directive. It automatically handles lazy loading, preconnect, and srcset. + +## 41. Hydration Mismatch +**Context:** SSR / SSG +### ❌ Bad Practice +Rendering `Date.now()` or random numbers (`Math.random()`) directly in the template. +### ⚠️ Problem +The server generates one number, the client another. This causes "flickering" and a hydration error; Angular discards the server DOM and renders from scratch. +### ✅ Best Practice +Use stable data or defer random generation until `afterNextRender`. +### 🚀 Solution +Pay attention to template determinism with SSR. + +## 42. Synchronous `inject()` inside loops +**Context:** DI Performance +### ❌ Bad Practice +Calling `inject()` inside a function that loops. +### ⚠️ Problem +Although `inject` is fast, in hot paths these are unnecessary DI tree lookups. +### ✅ Best Practice +Inject dependency once at the class/file constant level. + +## 43. Unused Signal Dependencies +**Context:** Signal Graph +### ❌ Bad Practice +Reading a signal inside `computed` whose value doesn't affect the result (an unexecuted logical branch). +### ⚠️ Problem +Angular dynamically builds the dependency graph. If you accidentally read a signal, it becomes a dependency. +### ✅ Best Practice +Use `untracked()` to read signals whose changes should not trigger a recalculation. + +## 44. Excessive Wrappers (`div` soup) +**Context:** DOM Size +### ❌ Bad Practice +```html +
    +``` +### ⚠️ Problem +Increases DOM tree depth, slowing down Style Recalculation and Layout. +### ✅ Best Practice +Use `` to group elements without creating extra DOM nodes. + +## 45. Neglecting `runOutsideAngular` for Events +**Context:** High-frequency events +### ❌ Bad Practice +`@HostListener('window:scroll')` +### ⚠️ Problem +Every scroll event triggers Change Detection. +### ✅ Best Practice +Subscribe manually in `runOutsideAngular` and update the signal only when necessary. + +--- diff --git a/frontend/angular/architecture.md b/frontend/angular/architecture.md new file mode 100644 index 0000000..d4738e3 --- /dev/null +++ b/frontend/angular/architecture.md @@ -0,0 +1,253 @@ +--- +description: Vibe coding guidelines and architectural constraints for Angular Architecture within the frontend domain. +technology: Angular +domain: frontend +level: Senior/Architect +version: "20" +tags: [architecture, dependency-injection, angular, best-practices, clean-code, scalable-code] +ai_role: Senior Angular Architecture Expert +last_updated: 2026-03-22 +--- + +# 🏗 Angular Architecture & Dependency Injection Best Practices + +# 📖 Context & Scope +- **Primary Goal:** Provide architectural best practices for Angular including DI usage, modules, routing, and guards. +- **Target Tooling:** Cursor, Windsurf, Antigravity. +- **Tech Stack Version:** Angular 20 + +## II. Architecture & DI (16-30) + +## 16. Services provided in 'root' vs Modules +**Context:** Tree Shaking +### ❌ Bad Practice +```typescript +@NgModule({ providers: [MyService] }) +``` +### ⚠️ Problem +The service is included in the bundle even if it is not used. +### ✅ Best Practice +```typescript +@Injectable({ providedIn: 'root' }) +``` +### 🚀 Solution +Always use `providedIn: 'root'`. This allows the bundler to remove unused services (Tree Shaking). + +## 17. Class-based Guards +**Context:** Routing Security +### ❌ Bad Practice +```typescript +@Injectable() +export class AuthGuard implements CanActivate { ... } +``` +### ⚠️ Problem +Class-based guards require more code and injections. They are less flexible for composition. +### ✅ Best Practice +```typescript +export const authGuard: CanActivateFn = (route, state) => { + return inject(AuthService).isLoggedIn(); +}; +``` +### 🚀 Solution +Use functional Guards (`CanActivateFn`). They are concise, easy to test, and composable. + +## 18. Class-based Interceptors +**Context:** HTTP Requests +### ❌ Bad Practice +```typescript +@Injectable() +export class TokenInterceptor implements HttpInterceptor { ... } +``` +### ⚠️ Problem +Similar to guards: lots of boilerplate, complex registration in the providers array. +### ✅ Best Practice +```typescript +export const tokenInterceptor: HttpInterceptorFn = (req, next) => { + const token = inject(AuthService).token(); + return next(req.clone({ setHeaders: { Authorization: token } })); +}; +``` +### 🚀 Solution +Use functional Interceptors (`HttpInterceptorFn`) with `provideHttpClient(withInterceptors([...]))`. + +## 19. State Mutation in Services +**Context:** Data Integrity +### ❌ Bad Practice +```typescript +updateUser(user: User) { + this.currentUser = user; // Mutable assignment +} +``` +### ⚠️ Problem +Object mutations complicate change tracking and can lead to unpredictable behavior in components using the `OnPush` strategy. +### ✅ Best Practice +```typescript +currentUser = signal(null); +updateUser(user: User) { + this.currentUser.set({ ...user }); // Immutable update +} +``` +### 🚀 Solution +Use Signals for state management. They guarantee reactivity and atomicity of updates. + +## 20. Calling functions inside `@for` tracking +**Context:** Rendering Performance +### ❌ Bad Practice +```html +@for (item of items; track getItemId(item)) +``` +### ⚠️ Problem +The tracking function is called for each element during every re-render. +### ✅ Best Practice +```html +@for (item of items; track item.id) +``` +### 🚀 Solution +Use an object property (ID or a unique key) directly. If a function is needed, it must be as simple and pure as possible. + +## 21. `host` property vs `@HostListener` +**Context:** Component Metadata +### ❌ Bad Practice +```typescript +@HostListener('click') onClick() { ... } +@HostBinding('class.active') isActive = true; +``` +### ⚠️ Problem +Decorators increase class size and scatter host configuration across the file. +### ✅ Best Practice +```typescript +@Component({ + host: { + '(click)': 'onClick()', + '[class.active]': 'isActive()' + } +}) +``` +### 🚀 Solution +Use the `host` property in component metadata. This centralizes all host element settings. + +## 22. Dynamic Components with `ComponentFactoryResolver` +**Context:** Dynamic Rendering +### ❌ Bad Practice +```typescript +const factory = this.resolver.resolveComponentFactory(MyComponent); +this.container.createComponent(factory); +``` +### ⚠️ Problem +`ComponentFactoryResolver` is deprecated. It is an old imperative API. +### ✅ Best Practice +```typescript +this.container.createComponent(MyComponent); +// Or strictly in template + +``` +### 🚀 Solution +Use `ViewContainerRef.createComponent` directly with the component class or the `ngComponentOutlet` directive. + +## 23. Shared Modules (The "Dump" Module) +**Context:** Modular Architecture +### ❌ Bad Practice +`SharedModule` imports and exports *all* UI components, pipes, and directives. +### ⚠️ Problem +If a component needs a single button, it is forced to pull the entire `SharedModule`. This breaks Tree Shaking and increases the initial bundle size. +### ✅ Best Practice +Import only what is needed directly into the `imports` of the Standalone component. +### 🚀 Solution +Abandon `SharedModule` in favor of granular imports of Standalone entities. + +## 24. Circular Dependencies in DI +**Context:** Architecture +### ❌ Bad Practice +Service A injects Service B, which injects Service A. +### ⚠️ Problem +Leads to runtime errors ("Cannot instantiate cyclic dependency"). Indicates poor architectural design. +### ✅ Best Practice +Use `forwardRef()` as a crutch, but it's better to extract the shared logic into a third Service C. +### 🚀 Solution +Refactoring: break services into smaller ones following SRP (Single Responsibility Principle). + +## 25. Logic in Pipes +**Context:** Separation of Concerns +### ❌ Bad Practice +A Pipe performs HTTP requests or complex business logic. +### ⚠️ Problem +Pipes are intended for data transformation in the template. Side effects in pipes violate function purity and kill CD performance. +### ✅ Best Practice +Pipes should be "Pure" (without side effects) and fast. +### 🚀 Solution +Extract logic into services/signals. Leave only formatting to pipes. + +## 26. `any` in Services +**Context:** TypeScript Safety +### ❌ Bad Practice +```typescript +getData(): Observable { ... } +``` +### ⚠️ Problem +`any` disables type checking, nullifying the benefits of TypeScript. Errors only surface at runtime. +### ✅ Best Practice +```typescript +getData(): Observable { ... } +``` +### 🚀 Solution +Use DTO interfaces (generate them from Swagger/OpenAPI) and Zod for API response validation. + +## 27. Multiple `async` pipes for same stream +**Context:** RxJS Subscriptions +### ❌ Bad Practice +```html +
    {{ (user$ | async).name }}
    +``` +### ⚠️ Problem +Each `async` pipe creates a new subscription. This can lead to duplicated HTTP requests. +### ✅ Best Practice +```html +@if (user$ | async; as user) { +
    {{ user.name }}
    +} +``` +### 🚀 Solution +Use aliases in the template (`as varName`) or convert the stream to a signal (`toSignal`). + +## 28. ProvidedIn 'any' +**Context:** DI Scopes +### ❌ Bad Practice +```typescript +@Injectable({ providedIn: 'any' }) +``` +### ⚠️ Problem +Creates a new service instance for each lazy-loaded module. This is often unexpected behavior, leading to state desynchronization (different singleton instances). +### ✅ Best Practice +`providedIn: 'root'` or providing at the level of a specific component (`providers: []`). +### 🚀 Solution +Avoid `any`. Explicitly control the scope: either global (`root`) or local. + +## 29. Imperative Routing +**Context:** Navigation +### ❌ Bad Practice +```typescript +this.router.navigateByUrl('/users/' + id); +``` +### ⚠️ Problem +Hardcoding route strings makes route refactoring a pain. +### ✅ Best Practice +```typescript +this.router.navigate(['users', id]); +``` +### 🚀 Solution +Use an array of segments. It is safer (automatic encoding of URL parameters) and cleaner. + +## 30. Ignoring `OnPush` Strategy +**Context:** Change Detection Strategy +### ❌ Bad Practice +Default components (`ChangeDetectionStrategy.Default`). +### ⚠️ Problem +Angular checks this component on *every* app event, even if the component data hasn't changed. +### ✅ Best Practice +```typescript +changeDetection: ChangeDetectionStrategy.OnPush +``` +### 🚀 Solution +Always set `OnPush`. With signals, this becomes the de facto standard, as updates occur precisely. + +--- diff --git a/frontend/angular/data-forms.md b/frontend/angular/data-forms.md new file mode 100644 index 0000000..9825242 --- /dev/null +++ b/frontend/angular/data-forms.md @@ -0,0 +1,136 @@ +--- +description: Vibe coding guidelines and architectural constraints for Angular Data & Forms within the frontend domain. +technology: Angular +domain: frontend +level: Senior/Architect +version: "20" +tags: [forms, data, angular, best-practices, clean-code, scalable-code] +ai_role: Senior Angular Data Expert +last_updated: 2026-03-22 +--- + +# 📝 Angular Data & Forms Best Practices + +# 📖 Context & Scope +- **Primary Goal:** Proper implementation of data management and forms in Angular applications. +- **Target Tooling:** Cursor, Windsurf, Antigravity. +- **Tech Stack Version:** Angular 20 + +## IV. Data & Forms (46-55) + +## 46. Template-Driven Forms without Types +**Context:** Form Safety +### ❌ Bad Practice +`[(ngModel)]` without strict model typing. +### ⚠️ Problem +Risk of assigning a string to a numeric field. +### ✅ Best Practice +Use Reactive Forms with `FormControl` typing or new Signal-based Forms (when out of developer preview). + +## 47. Untyped `FormGroup` +**Context:** Reactive Forms +### ❌ Bad Practice +```typescript +const form = new FormGroup({ ... }); // Untyped +``` +### ⚠️ Problem +`form.value` returns `any`. +### ✅ Best Practice +```typescript +const form = new FormGroup({ + email: new FormControl('', { nonNullable: true }), + ... +}); +``` +### 🚀 Solution +Always type forms. Use `nonNullable: true` to avoid `string | undefined` hell. + +## 48. Subscribe inside Subscribe +**Context:** RxJS Patterns +### ❌ Bad Practice +```typescript +this.route.params.subscribe(params => { + this.api.getUser(params.id).subscribe(user => ...); +}); +``` +### ⚠️ Problem +Classic Race Condition. If parameters change rapidly, response order is not guaranteed. +### ✅ Best Practice +```typescript +this.route.params.pipe( + switchMap(params => this.api.getUser(params.id)) +).subscribe(); +``` +### 🚀 Solution +Use Flattening Operators (`switchMap`, `concatMap`, `mergeMap`). + +## 49. Ignoring `AbortSignal` in HTTP +**Context:** Network Efficiency +### ❌ Bad Practice +Ignoring request cancellation when navigating away from the page. +### ⚠️ Problem +Requests continue hanging, consuming traffic. +### ✅ Best Practice +HttpClient automatically supports cancellation upon unsubscription. With signals: ensure `rxResource` or the effect correctly cancels the request. + +## 50. Mutating Inputs directly +**Context:** Unidirectional Data Flow +### ❌ Bad Practice +```typescript +this.inputData.push(newItem); +``` +### ⚠️ Problem +The parent component remains unaware of the change. Violates the One-Way Data Flow principle. +### ✅ Best Practice +Emit event (`output`) upwards; the parent changes the data and passes the new object downwards. + +## 51. `ngModel` inside Reactive Form +**Context:** Form Mixing +### ❌ Bad Practice +Using `formControlName` and `[(ngModel)]` on the same input. +### ⚠️ Problem +Deprecated behavior. Causes form and model synchronization conflicts. +### ✅ Best Practice +Use only one approach: either Reactive or Template-driven. + +## 52. Complex Validators in Template +**Context:** Form Logic +### ❌ Bad Practice +Validation via HTML attributes for complex logic. +### ⚠️ Problem +Hard to test, no reusability. +### ✅ Best Practice +Custom Validator Functions or Async Validators in the component class. + +## 53. Forgetting `updateOn: 'blur'` +**Context:** Performance +### ❌ Bad Practice +Validating a complex field on every keystroke (`change`). +### ⚠️ Problem +Slows down user input. +### ✅ Best Practice +```typescript +new FormControl('', { updateOn: 'blur' }); +``` +### 🚀 Solution +Trigger validation/update only when the user has finished typing. + +## 54. Not handling API Errors +**Context:** UX +### ❌ Bad Practice +`.subscribe(data => ...)` without an error callback. +### ⚠️ Problem +On a 500 error, the application "hangs" in a loading state. +### ✅ Best Practice +Global Error Handler or `catchError` in the pipe returning a safe value. + +## 55. Hardcoded API URLs +**Context:** Maintainability +### ❌ Bad Practice +`http.get('https://api.com/users')` +### ⚠️ Problem +Inability to switch environments (dev/prod). +### ✅ Best Practice +Using InjectionToken `API_URL` and environment configuration. + +--- diff --git a/frontend/angular/expert-niche.md b/frontend/angular/expert-niche.md new file mode 100644 index 0000000..fd263e8 --- /dev/null +++ b/frontend/angular/expert-niche.md @@ -0,0 +1,88 @@ +--- +description: Vibe coding guidelines and architectural constraints for Angular Expert/Niche topics within the frontend domain. +technology: Angular +domain: frontend +level: Senior/Architect +version: "20" +tags: [expert, niche, angular, best-practices, clean-code, scalable-code] +ai_role: Senior Angular Expert +last_updated: 2026-03-22 +--- + +# 🧠 Angular Expert/Niche Best Practices + +# 📖 Context & Scope +- **Primary Goal:** Deep-dive into expert and niche topics in Angular. +- **Target Tooling:** Cursor, Windsurf, Antigravity. +- **Tech Stack Version:** Angular 20 + +## V. Expert/Niche (56-60) + +## 56. `untracked()` usage +**Context:** Fine-grained Reactivity +### ❌ Bad Practice +Accidentally creating a cyclic dependency in `computed`. +### ⚠️ Problem +`Error: Detected cycle in computations`. +### ✅ Best Practice +```typescript +computed(() => { + const user = this.user(); + untracked(() => this.logger.log(user)); // Logging doesn't create dependency + return user.name; +}); +``` +### 🚀 Solution +Use `untracked()` for side effects or reads that shouldn't affect recalculation. + +## 57. V8 Hidden Classes Optimization +**Context:** Micro-optimization +### ❌ Bad Practice +```typescript +user = signal({}); +// later +user.set({ name: 'A', age: 10 }); // Shape change +``` +### ⚠️ Problem +Initializing with an empty object and later adding fields changes the object "shape" (Hidden Class), breaking V8 JIT compiler optimization. +### ✅ Best Practice +```typescript +interface User { name: string | null; age: number | null; } +user = signal({ name: null, age: null }); +``` +### 🚀 Solution +Always initialize signals with the full object shape (even with null) to preserve property access monomorphism. + +## 58. Signal Glitch Freedom abuse +**Context:** Reactivity Theory +### ❌ Bad Practice +Relying on `effect` to fire synchronously. +### ⚠️ Problem +Signals guarantee "Glitch Freedom" (absence of intermediate inconsistent states), but effects are asynchronous (microtask timing). +### ✅ Best Practice +Do not use effects to synchronize local state. Use `computed`. + +## 59. Memory leaks in `root` Effects +**Context:** Application Lifecycle +### ❌ Bad Practice +Creating an effect in a service without `manualCleanup`. +### ⚠️ Problem +Effects in `root` services live forever. If they subscribe to something global, it can leak. +### ✅ Best Practice +Usually fine, but if the service is destroyed (rare lazy loading case), the effect must be cleaned up with `effectRef.destroy()`. + +## 60. `runInInjectionContext` +**Context:** Advanced DI +### ❌ Bad Practice +Passing an `Injector` instance manually into functions. +### ⚠️ Problem +Bulky code. +### ✅ Best Practice +```typescript +runInInjectionContext(this.injector, () => { + // can use inject() here dynamically + const service = inject(MyService); +}); +``` +### 🚀 Solution +Use this helper to execute functions requiring a DI context outside the constructor/initialization. diff --git a/frontend/angular/readme.md b/frontend/angular/readme.md index 546cdf7..8d43d1e 100644 --- a/frontend/angular/readme.md +++ b/frontend/angular/readme.md @@ -9,9 +9,9 @@ ai_role: Senior Angular Performance Expert last_updated: 2026-03-22 --- -# Angular Best Practices & Production-Ready Patterns +# 🎨 Angular Best Practices & Production-Ready Patterns -# Context & Scope +# 🎨 Context & Scope - **Primary Goal:** Enforce strict adherence to modern Angular v20 patterns, specifically Zoneless reactivity and functional APIs for optimal best practices. - **Target Tooling:** Cursor, Windsurf, Antigravity. - **Tech Stack Version:** Angular 20 @@ -290,627 +290,12 @@ Migrate to Zoneless mode. Use Signals to notify Angular when a re-render is need --- -## II. Architecture & DI (16-30) -## 16. Services provided in 'root' vs Modules -**Context:** Tree Shaking -### ❌ Bad Practice -```typescript -@NgModule({ providers: [MyService] }) -``` -### ⚠️ Problem -The service is included in the bundle even if it is not used. -### ✅ Best Practice -```typescript -@Injectable({ providedIn: 'root' }) -``` -### 🚀 Solution -Always use `providedIn: 'root'`. This allows the bundler to remove unused services (Tree Shaking). - -## 17. Class-based Guards -**Context:** Routing Security -### ❌ Bad Practice -```typescript -@Injectable() -export class AuthGuard implements CanActivate { ... } -``` -### ⚠️ Problem -Class-based guards require more code and injections. They are less flexible for composition. -### ✅ Best Practice -```typescript -export const authGuard: CanActivateFn = (route, state) => { - return inject(AuthService).isLoggedIn(); -}; -``` -### 🚀 Solution -Use functional Guards (`CanActivateFn`). They are concise, easy to test, and composable. - -## 18. Class-based Interceptors -**Context:** HTTP Requests -### ❌ Bad Practice -```typescript -@Injectable() -export class TokenInterceptor implements HttpInterceptor { ... } -``` -### ⚠️ Problem -Similar to guards: lots of boilerplate, complex registration in the providers array. -### ✅ Best Practice -```typescript -export const tokenInterceptor: HttpInterceptorFn = (req, next) => { - const token = inject(AuthService).token(); - return next(req.clone({ setHeaders: { Authorization: token } })); -}; -``` -### 🚀 Solution -Use functional Interceptors (`HttpInterceptorFn`) with `provideHttpClient(withInterceptors([...]))`. - -## 19. State Mutation in Services -**Context:** Data Integrity -### ❌ Bad Practice -```typescript -updateUser(user: User) { - this.currentUser = user; // Mutable assignment -} -``` -### ⚠️ Problem -Object mutations complicate change tracking and can lead to unpredictable behavior in components using the `OnPush` strategy. -### ✅ Best Practice -```typescript -currentUser = signal(null); -updateUser(user: User) { - this.currentUser.set({ ...user }); // Immutable update -} -``` -### 🚀 Solution -Use Signals for state management. They guarantee reactivity and atomicity of updates. - -## 20. Calling functions inside `@for` tracking -**Context:** Rendering Performance -### ❌ Bad Practice -```html -@for (item of items; track getItemId(item)) -``` -### ⚠️ Problem -The tracking function is called for each element during every re-render. -### ✅ Best Practice -```html -@for (item of items; track item.id) -``` -### 🚀 Solution -Use an object property (ID or a unique key) directly. If a function is needed, it must be as simple and pure as possible. - -## 21. `host` property vs `@HostListener` -**Context:** Component Metadata -### ❌ Bad Practice -```typescript -@HostListener('click') onClick() { ... } -@HostBinding('class.active') isActive = true; -``` -### ⚠️ Problem -Decorators increase class size and scatter host configuration across the file. -### ✅ Best Practice -```typescript -@Component({ - host: { - '(click)': 'onClick()', - '[class.active]': 'isActive()' - } -}) -``` -### 🚀 Solution -Use the `host` property in component metadata. This centralizes all host element settings. - -## 22. Dynamic Components with `ComponentFactoryResolver` -**Context:** Dynamic Rendering -### ❌ Bad Practice -```typescript -const factory = this.resolver.resolveComponentFactory(MyComponent); -this.container.createComponent(factory); -``` -### ⚠️ Problem -`ComponentFactoryResolver` is deprecated. It is an old imperative API. -### ✅ Best Practice -```typescript -this.container.createComponent(MyComponent); -// Or strictly in template - -``` -### 🚀 Solution -Use `ViewContainerRef.createComponent` directly with the component class or the `ngComponentOutlet` directive. - -## 23. Shared Modules (The "Dump" Module) -**Context:** Modular Architecture -### ❌ Bad Practice -`SharedModule` imports and exports *all* UI components, pipes, and directives. -### ⚠️ Problem -If a component needs a single button, it is forced to pull the entire `SharedModule`. This breaks Tree Shaking and increases the initial bundle size. -### ✅ Best Practice -Import only what is needed directly into the `imports` of the Standalone component. -### 🚀 Solution -Abandon `SharedModule` in favor of granular imports of Standalone entities. - -## 24. Circular Dependencies in DI -**Context:** Architecture -### ❌ Bad Practice -Service A injects Service B, which injects Service A. -### ⚠️ Problem -Leads to runtime errors ("Cannot instantiate cyclic dependency"). Indicates poor architectural design. -### ✅ Best Practice -Use `forwardRef()` as a crutch, but it's better to extract the shared logic into a third Service C. -### 🚀 Solution -Refactoring: break services into smaller ones following SRP (Single Responsibility Principle). - -## 25. Logic in Pipes -**Context:** Separation of Concerns -### ❌ Bad Practice -A Pipe performs HTTP requests or complex business logic. -### ⚠️ Problem -Pipes are intended for data transformation in the template. Side effects in pipes violate function purity and kill CD performance. -### ✅ Best Practice -Pipes should be "Pure" (without side effects) and fast. -### 🚀 Solution -Extract logic into services/signals. Leave only formatting to pipes. - -## 26. `any` in Services -**Context:** TypeScript Safety -### ❌ Bad Practice -```typescript -getData(): Observable { ... } -``` -### ⚠️ Problem -`any` disables type checking, nullifying the benefits of TypeScript. Errors only surface at runtime. -### ✅ Best Practice -```typescript -getData(): Observable { ... } -``` -### 🚀 Solution -Use DTO interfaces (generate them from Swagger/OpenAPI) and Zod for API response validation. - -## 27. Multiple `async` pipes for same stream -**Context:** RxJS Subscriptions -### ❌ Bad Practice -```html -
    {{ (user$ | async).name }}
    -``` -### ⚠️ Problem -Each `async` pipe creates a new subscription. This can lead to duplicated HTTP requests. -### ✅ Best Practice -```html -@if (user$ | async; as user) { -
    {{ user.name }}
    -} -``` -### 🚀 Solution -Use aliases in the template (`as varName`) or convert the stream to a signal (`toSignal`). - -## 28. ProvidedIn 'any' -**Context:** DI Scopes -### ❌ Bad Practice -```typescript -@Injectable({ providedIn: 'any' }) -``` -### ⚠️ Problem -Creates a new service instance for each lazy-loaded module. This is often unexpected behavior, leading to state desynchronization (different singleton instances). -### ✅ Best Practice -`providedIn: 'root'` or providing at the level of a specific component (`providers: []`). -### 🚀 Solution -Avoid `any`. Explicitly control the scope: either global (`root`) or local. - -## 29. Imperative Routing -**Context:** Navigation -### ❌ Bad Practice -```typescript -this.router.navigateByUrl('/users/' + id); -``` -### ⚠️ Problem -Hardcoding route strings makes route refactoring a pain. -### ✅ Best Practice -```typescript -this.router.navigate(['users', id]); -``` -### 🚀 Solution -Use an array of segments. It is safer (automatic encoding of URL parameters) and cleaner. - -## 30. Ignoring `OnPush` Strategy -**Context:** Change Detection Strategy -### ❌ Bad Practice -Default components (`ChangeDetectionStrategy.Default`). -### ⚠️ Problem -Angular checks this component on *every* app event, even if the component data hasn't changed. -### ✅ Best Practice -```typescript -changeDetection: ChangeDetectionStrategy.OnPush -``` -### 🚀 Solution -Always set `OnPush`. With signals, this becomes the de facto standard, as updates occur precisely. - ---- - -## III. Advanced Performance (31-45) - -## 31. Eager Loading of Heavy Components -**Context:** Bundle Size -### ❌ Bad Practice -```html - -``` -### ⚠️ Problem -A charting library (e.g., ECharts) loads immediately, blocking TTI (Time to Interactive), even if the chart is below the "fold". -### ✅ Best Practice -```html -@defer (on viewport) { - -} @placeholder { -
    Loading chart...
    -} -``` -### 🚀 Solution -Use `@defer`. This defers component code loading until a trigger occurs (viewport, interaction, timer). - -## 32. Heavy Computation in Main Thread -**Context:** Event Loop Blocking -### ❌ Bad Practice -Sorting an array of 100k elements directly in the component. -### ⚠️ Problem -Freezes the UI. -### ✅ Best Practice -Offload computations to a Web Worker. -### 🚀 Solution -Use Angular Web Workers. In v20, this is easily configured via the CLI. - -## 33. Memory Leaks in `effect()` -**Context:** Signal Effects -### ❌ Bad Practice -```typescript -effect(() => { - const timer = setInterval(() => ..., 1000); - // No cleanup -}); -``` -### ⚠️ Problem -Effects restart when dependencies change. If you don't clean up timers/subscriptions inside an effect, they accumulate. -### ✅ Best Practice -```typescript -effect((onCleanup) => { - const timer = setInterval(() => ..., 1000); - onCleanup(() => clearInterval(timer)); -}); -``` -### 🚀 Solution -Always use the `onCleanup` callback to release resources. - -## 34. Excessive Change Detection with `NgZone.run()` -**Context:** Zone Integration -### ❌ Bad Practice -Wrapping third-party libraries in `ngZone.run()` unnecessarily. -### ⚠️ Problem -Forces redundant checks of the entire component tree. -### ✅ Best Practice -```typescript -ngZone.runOutsideAngular(() => { - // Heavy chart rendering or canvas animation -}); -``` -### 🚀 Solution -Run frequent events (scroll, mousemove, animationFrame) *outside* the Angular zone. Update signals only when UI updates are required. - -## 35. Signals equality check default -**Context:** Signal Performance -### ❌ Bad Practice -```typescript -data = signal({ id: 1 }, { equal: undefined }); // Default checks reference -``` -### ⚠️ Problem -If you create a new object with the same data `{ id: 1 }`, the signal triggers an update, even though the data hasn't fundamentally changed. -### ✅ Best Practice -```typescript -import { isEqual } from 'lodash-es'; -data = signal(obj, { equal: isEqual }); -``` -### 🚀 Solution -Use a custom comparison function for complex objects to avoid redundant re-renders. - -## 36. Lacking `trackBy` in iterables -**Context:** Re-rendering Lists -### ❌ Bad Practice -```html -
  • {{ item }}
  • -``` -### ⚠️ Problem -Without tracking, any array change leads to the recreation of all DOM nodes in the list. $O(n)$ DOM operations. -### ✅ Best Practice -```html -@for (item of items; track item.id) -``` -### 🚀 Solution -Always use a unique key in `track`. This allows Angular to move DOM nodes instead of recreating them. - -## 37. Recursive Template without Caching -**Context:** Tree Rendering -### ❌ Bad Practice -Recursive component call without `OnPush` and memoization. -### ⚠️ Problem -Exponential growth in change detection checks. -### ✅ Best Practice -Using the `Memoization` pattern or `computed()` to prepare the tree data structure. - -## 38. Global Styles Leakage -**Context:** CSS Encapsulation -### ❌ Bad Practice -```css -/* global.css */ -button { padding: 10px; } -``` -### ⚠️ Problem -Global styles unpredictably affect components. -### ✅ Best Practice -Use `ViewEncapsulation.Emulated` (default) and specific selectors. -### 🚀 Solution -Keep styles locally within components. - -## 39. Large Component Bundle -**Context:** Split Chunks -### ❌ Bad Practice -A single huge component of 3000 lines. -### ⚠️ Problem -Poor readability, rendering lazy loading of UI parts impossible. -### ✅ Best Practice -Decompose into "dumb" (UI) and "smart" components. -### 🚀 Solution -Break down the UI into small, reusable blocks. - -## 40. Image Optimization Ignorance -**Context:** Core Web Vitals (LCP) -### ❌ Bad Practice -```html - -``` -### ⚠️ Problem -The browser loads the full image, shifting the layout (CLS). -### ✅ Best Practice -```html - -``` -### 🚀 Solution -Use the `NgOptimizedImage` directive. It automatically handles lazy loading, preconnect, and srcset. - -## 41. Hydration Mismatch -**Context:** SSR / SSG -### ❌ Bad Practice -Rendering `Date.now()` or random numbers (`Math.random()`) directly in the template. -### ⚠️ Problem -The server generates one number, the client another. This causes "flickering" and a hydration error; Angular discards the server DOM and renders from scratch. -### ✅ Best Practice -Use stable data or defer random generation until `afterNextRender`. -### 🚀 Solution -Pay attention to template determinism with SSR. - -## 42. Synchronous `inject()` inside loops -**Context:** DI Performance -### ❌ Bad Practice -Calling `inject()` inside a function that loops. -### ⚠️ Problem -Although `inject` is fast, in hot paths these are unnecessary DI tree lookups. -### ✅ Best Practice -Inject dependency once at the class/file constant level. - -## 43. Unused Signal Dependencies -**Context:** Signal Graph -### ❌ Bad Practice -Reading a signal inside `computed` whose value doesn't affect the result (an unexecuted logical branch). -### ⚠️ Problem -Angular dynamically builds the dependency graph. If you accidentally read a signal, it becomes a dependency. -### ✅ Best Practice -Use `untracked()` to read signals whose changes should not trigger a recalculation. - -## 44. Excessive Wrappers (`div` soup) -**Context:** DOM Size -### ❌ Bad Practice -```html -
    -``` -### ⚠️ Problem -Increases DOM tree depth, slowing down Style Recalculation and Layout. -### ✅ Best Practice -Use `` to group elements without creating extra DOM nodes. - -## 45. Neglecting `runOutsideAngular` for Events -**Context:** High-frequency events -### ❌ Bad Practice -`@HostListener('window:scroll')` -### ⚠️ Problem -Every scroll event triggers Change Detection. -### ✅ Best Practice -Subscribe manually in `runOutsideAngular` and update the signal only when necessary. - ---- - -## IV. Data & Forms (46-55) - -## 46. Template-Driven Forms without Types -**Context:** Form Safety -### ❌ Bad Practice -`[(ngModel)]` without strict model typing. -### ⚠️ Problem -Risk of assigning a string to a numeric field. -### ✅ Best Practice -Use Reactive Forms with `FormControl` typing or new Signal-based Forms (when out of developer preview). - -## 47. Untyped `FormGroup` -**Context:** Reactive Forms -### ❌ Bad Practice -```typescript -const form = new FormGroup({ ... }); // Untyped -``` -### ⚠️ Problem -`form.value` returns `any`. -### ✅ Best Practice -```typescript -const form = new FormGroup({ - email: new FormControl('', { nonNullable: true }), - ... -}); -``` -### 🚀 Solution -Always type forms. Use `nonNullable: true` to avoid `string | undefined` hell. - -## 48. Subscribe inside Subscribe -**Context:** RxJS Patterns -### ❌ Bad Practice -```typescript -this.route.params.subscribe(params => { - this.api.getUser(params.id).subscribe(user => ...); -}); -``` -### ⚠️ Problem -Classic Race Condition. If parameters change rapidly, response order is not guaranteed. -### ✅ Best Practice -```typescript -this.route.params.pipe( - switchMap(params => this.api.getUser(params.id)) -).subscribe(); -``` -### 🚀 Solution -Use Flattening Operators (`switchMap`, `concatMap`, `mergeMap`). - -## 49. Ignoring `AbortSignal` in HTTP -**Context:** Network Efficiency -### ❌ Bad Practice -Ignoring request cancellation when navigating away from the page. -### ⚠️ Problem -Requests continue hanging, consuming traffic. -### ✅ Best Practice -HttpClient automatically supports cancellation upon unsubscription. With signals: ensure `rxResource` or the effect correctly cancels the request. - -## 50. Mutating Inputs directly -**Context:** Unidirectional Data Flow -### ❌ Bad Practice -```typescript -this.inputData.push(newItem); -``` -### ⚠️ Problem -The parent component remains unaware of the change. Violates the One-Way Data Flow principle. -### ✅ Best Practice -Emit event (`output`) upwards; the parent changes the data and passes the new object downwards. - -## 51. `ngModel` inside Reactive Form -**Context:** Form Mixing -### ❌ Bad Practice -Using `formControlName` and `[(ngModel)]` on the same input. -### ⚠️ Problem -Deprecated behavior. Causes form and model synchronization conflicts. -### ✅ Best Practice -Use only one approach: either Reactive or Template-driven. - -## 52. Complex Validators in Template -**Context:** Form Logic -### ❌ Bad Practice -Validation via HTML attributes for complex logic. -### ⚠️ Problem -Hard to test, no reusability. -### ✅ Best Practice -Custom Validator Functions or Async Validators in the component class. - -## 53. Forgetting `updateOn: 'blur'` -**Context:** Performance -### ❌ Bad Practice -Validating a complex field on every keystroke (`change`). -### ⚠️ Problem -Slows down user input. -### ✅ Best Practice -```typescript -new FormControl('', { updateOn: 'blur' }); -``` -### 🚀 Solution -Trigger validation/update only when the user has finished typing. - -## 54. Not handling API Errors -**Context:** UX -### ❌ Bad Practice -`.subscribe(data => ...)` without an error callback. -### ⚠️ Problem -On a 500 error, the application "hangs" in a loading state. -### ✅ Best Practice -Global Error Handler or `catchError` in the pipe returning a safe value. - -## 55. Hardcoded API URLs -**Context:** Maintainability -### ❌ Bad Practice -`http.get('https://api.com/users')` -### ⚠️ Problem -Inability to switch environments (dev/prod). -### ✅ Best Practice -Using InjectionToken `API_URL` and environment configuration. - ---- - -## V. Expert/Niche (56-60) - -## 56. `untracked()` usage -**Context:** Fine-grained Reactivity -### ❌ Bad Practice -Accidentally creating a cyclic dependency in `computed`. -### ⚠️ Problem -`Error: Detected cycle in computations`. -### ✅ Best Practice -```typescript -computed(() => { - const user = this.user(); - untracked(() => this.logger.log(user)); // Logging doesn't create dependency - return user.name; -}); -``` -### 🚀 Solution -Use `untracked()` for side effects or reads that shouldn't affect recalculation. - -## 57. V8 Hidden Classes Optimization -**Context:** Micro-optimization -### ❌ Bad Practice -```typescript -user = signal({}); -// later -user.set({ name: 'A', age: 10 }); // Shape change -``` -### ⚠️ Problem -Initializing with an empty object and later adding fields changes the object "shape" (Hidden Class), breaking V8 JIT compiler optimization. -### ✅ Best Practice -```typescript -interface User { name: string | null; age: number | null; } -user = signal({ name: null, age: null }); -``` -### 🚀 Solution -Always initialize signals with the full object shape (even with null) to preserve property access monomorphism. - -## 58. Signal Glitch Freedom abuse -**Context:** Reactivity Theory -### ❌ Bad Practice -Relying on `effect` to fire synchronously. -### ⚠️ Problem -Signals guarantee "Glitch Freedom" (absence of intermediate inconsistent states), but effects are asynchronous (microtask timing). -### ✅ Best Practice -Do not use effects to synchronize local state. Use `computed`. +## 📚 Specialized Topics -## 59. Memory leaks in `root` Effects -**Context:** Application Lifecycle -### ❌ Bad Practice -Creating an effect in a service without `manualCleanup`. -### ⚠️ Problem -Effects in `root` services live forever. If they subscribe to something global, it can leak. -### ✅ Best Practice -Usually fine, but if the service is destroyed (rare lazy loading case), the effect must be cleaned up with `effectRef.destroy()`. +For further reading, please refer to the following specialized guides: -## 60. `runInInjectionContext` -**Context:** Advanced DI -### ❌ Bad Practice -Passing an `Injector` instance manually into functions. -### ⚠️ Problem -Bulky code. -### ✅ Best Practice -```typescript -runInInjectionContext(this.injector, () => { - // can use inject() here dynamically - const service = inject(MyService); -}); -``` -### 🚀 Solution -Use this helper to execute functions requiring a DI context outside the constructor/initialization. +- [🏗 Architecture & Dependency Injection](./architecture.md) +- [🚀 Advanced Performance](./advanced-performance.md) +- [📝 Data & Forms](./data-forms.md) +- [🧠 Expert/Niche Topics](./expert-niche.md) diff --git a/frontend/javascript/async-logic.md b/frontend/javascript/async-logic.md new file mode 100644 index 0000000..cc08d5b --- /dev/null +++ b/frontend/javascript/async-logic.md @@ -0,0 +1,230 @@ +--- +description: Vibe coding guidelines and architectural constraints for JavaScript Asynchronous Logic within the frontend domain. +technology: JavaScript +domain: frontend +level: Senior/Architect +version: "ES2024+" +tags: [javascript, async, promises, best-practices, clean-code, scalable-code] +ai_role: Senior JavaScript Asynchronous Expert +last_updated: 2026-03-22 +--- + +# ⏳ JavaScript Asynchronous & Logic Best Practices + +# 📖 Context & Scope +- **Primary Goal:** Implement correct and robust asynchronous logic in JavaScript applications. +- **Target Tooling:** Cursor, Windsurf, Antigravity. +- **Tech Stack Version:** ES2024+ + +## III. Asynchronous & Logic + +## 21. Callback Hell vs Promises +**Context:** Managing asynchronous execution flow. +### ❌ Bad Practice +```javascript +getData(url, (err, res) => { + getDetails(res.id, (err, details) => { + saveData(details, (err, ok) => { + // Callback Hell + }); + }); +}); +``` +### ⚠️ Problem +Deeply nested callbacks (the "Pyramid of Doom") make error handling extremely difficult and code unreadable. +### ✅ Best Practice +```javascript +fetchData(url) + .then(res => fetchDetails(res.id)) + .then(details => saveData(details)) + .catch(err => handleError(err)); +``` +### 🚀 Solution +Use Promises to flatten the structure and centralize error handling with `.catch()`. + +## 22. Promise.then() nesting vs Async/Await +**Context:** Modern syntax for asynchronous code. +### ❌ Bad Practice +```javascript +function load() { + return api.get().then(res => { + return api.process(res).then(processed => { + return processed; + }); + }); +} +``` +### ⚠️ Problem +Even with Promises, `.then()` nesting can occur. It still feels like "callback style" logic. +### ✅ Best Practice +```javascript +async function load() { + const res = await api.get(); + const processed = await api.process(res); + return processed; +} +``` +### 🚀 Solution +Use `async/await`. It allows asynchronous code to be written and read like synchronous code, improving maintainability. + +## 23. Sequential `await` in loops vs `Promise.all` +**Context:** Parallelizing independent asynchronous operations. +### ❌ Bad Practice +```javascript +for (const id of ids) { + await fetchItem(id); // Pauses loop for each request +} +``` +### ⚠️ Problem +Sequential `await` in a loop causes a "waterfall" effect, where each request waits for the previous one to finish, significantly increasing total execution time. +### ✅ Best Practice +```javascript +const promises = ids.map(id => fetchItem(id)); +await Promise.all(promises); +``` +### 🚀 Solution +Use `Promise.all` to execute independent promises in parallel. This utilizes the full network/IO bandwidth. + +## 24. Missing `try/catch` in async +**Context:** Handling failures in async functions. +### ❌ Bad Practice +```javascript +async function getData() { + const data = await fetch(url); // If this fails, the process might crash + return data; +} +``` +### ⚠️ Problem +Unhandled exceptions in `async` functions result in unhandled promise rejections, which can lead to silent failures or process termination in Node.js. +### ✅ Best Practice +```javascript +async function getData() { + try { + const data = await fetch(url); + return data; + } catch (error) { + logError(error); + } +} +``` +### 🚀 Solution +Wrap `await` calls in `try/catch` blocks or use a higher-order function to catch errors. + +## 25. Floating point math errors (`0.1 + 0.2`) +**Context:** Precision issues in IEEE 754 arithmetic. +### ❌ Bad Practice +```javascript +if (0.1 + 0.2 === 0.3) { /* False! */ } +``` +### ⚠️ Problem +$\text{0.1} + \text{0.2} = \text{0.30000000000000004}$ due to binary representation limits. This leads to critical bugs in financial or scientific applications. +### ✅ Best Practice +```javascript +const EPSILON = Number.EPSILON; +const areEqual = (a, b) => Math.abs(a - b) < EPSILON; + +// Or for money: +const totalCents = (10 + 20); // 30 cents +``` +### 🚀 Solution +Use `Number.EPSILON` for comparisons or represent decimals as integers (e.g., cents instead of dollars) to avoid floating point drift. + +## 26. Multiple Boolean flags vs State Machine +**Context:** Managing complex component logic. +### ❌ Bad Practice +```javascript +const [isLoading, setIsLoading] = useState(false); +const [isError, setIsError] = useState(false); +const [isSuccess, setIsSuccess] = useState(false); +``` +### ⚠️ Problem +Multiple flags allow for "impossible states" (e.g., `isLoading` and `isError` both being `true`). This makes logic branches exponentially complex. +### ✅ Best Practice +```javascript +const [status, setStatus] = useState('IDLE'); // IDLE, LOADING, ERROR, SUCCESS +``` +### 🚀 Solution +Use a single state variable or a state machine. This ensures only one state is active at a time and simplifies transitions. + +## 27. Sync logic in Event Loop +**Context:** Keeping the UI responsive. +### ❌ Bad Practice +```javascript +function processLargeArray(arr) { + // Blocks the main thread for 2 seconds + arr.sort().forEach(item => complexCalc(item)); +} +``` +### ⚠️ Problem +JavaScript is single-threaded. Heavy synchronous computation blocks the Event Loop, causing the UI to freeze and preventing user interaction. +### ✅ Best Practice +```javascript +// Use Web Workers or break into chunks +function processInChunks(arr) { + if (arr.length === 0) return; + const chunk = arr.splice(0, 100); + process(chunk); + setTimeout(() => processInChunks(arr), 0); +} +``` +### 🚀 Solution +Offload heavy tasks to Web Workers or use `requestIdleCallback`/`setTimeout` to break long tasks into smaller chunks, allowing the browser to render between frames. + +## 28. Overusing `classes` where functions suffice +**Context:** Paradigm choice (OOP vs FP). +### ❌ Bad Practice +```javascript +class Calculator { + add(a, b) { return a + b; } +} +const calc = new Calculator(); +``` +### ⚠️ Problem +Classes introduce unnecessary overhead (prototype chain, `this` binding issues) and make tree-shaking harder for bundlers. +### ✅ Best Practice +```javascript +export const add = (a, b) => a + b; +``` +### 🚀 Solution +Use simple functions and modules for logic. Use classes only when you need to manage complex stateful instances with shared behavior. + +## 29. Hard-coded Error messages vs Error Classes +**Context:** Robust error handling and debugging. +### ❌ Bad Practice +```javascript +throw new Error('User not found'); +``` +### ⚠️ Problem +Parsing error messages in `catch` blocks is brittle. If the string changes, the error handling logic breaks. +### ✅ Best Practice +```javascript +class UserNotFoundError extends Error { + constructor(userId) { + super(`User ${userId} not found`); + this.name = 'UserNotFoundError'; + this.code = 404; + } +} +``` +### 🚀 Solution +Extend the `Error` class to create custom error types. Use `instanceof` check in catch blocks to handle specific errors differently. + +## 30. Unhandled Rejections +**Context:** Reliability of asynchronous flows. +### ❌ Bad Practice +```javascript +// No .catch() or try/catch +fetch('/api/data'); +``` +### ⚠️ Problem +Unhandled rejections create silent failures. In production environments, this can lead to memory leaks as the promise state stays pending or rejected without being cleared. +### ✅ Best Practice +```javascript +window.addEventListener('unhandledrejection', event => { + reportToSentry(event.reason); +}); +``` +### 🚀 Solution +Always handle promise rejections. Implement a global unhandled rejection listener as a safety net for monitoring. + +--- diff --git a/frontend/javascript/modern-syntax.md b/frontend/javascript/modern-syntax.md new file mode 100644 index 0000000..e525070 --- /dev/null +++ b/frontend/javascript/modern-syntax.md @@ -0,0 +1,204 @@ +--- +description: Vibe coding guidelines and architectural constraints for Modern JavaScript Syntax & FP within the frontend domain. +technology: JavaScript +domain: frontend +level: Senior/Architect +version: "ES2024+" +tags: [javascript, es6, functional-programming, best-practices, clean-code, scalable-code] +ai_role: Senior JavaScript Expert +last_updated: 2026-03-22 +--- + +# ✨ Modern JavaScript Syntax & Functional Programming Best Practices + +# 📖 Context & Scope +- **Primary Goal:** Enforce strict adherence to modern ES6+ syntax and functional programming patterns. +- **Target Tooling:** Cursor, Windsurf, Antigravity. +- **Tech Stack Version:** ES2024+ + +## II. Modern Syntax & FP (ES6-ES2024) + +## 11. Manual object property assignment vs Shorthand +**Context:** Reducing boilerplate in object creation. +### ❌ Bad Practice +```javascript +const name = 'Alice'; +const user = { + name: name, + age: age +}; +``` +### ⚠️ Problem +Redundant repetition of keys and values increases file size and makes the code noisier. +### ✅ Best Practice +```javascript +const name = 'Alice'; +const user = { name, age }; +``` +### 🚀 Solution +Use Property Shorthand. When the key and variable name match, omit the value. + +## 12. Using `arguments` vs Rest parameters +**Context:** Handling variable numbers of arguments. +### ❌ Bad Practice +```javascript +function sum() { + const args = Array.prototype.slice.call(arguments); + return args.reduce((a, b) => a + b); +} +``` +### ⚠️ Problem +The `arguments` object is not a real array (it lacks methods like `map` or `reduce`). It is also incompatible with arrow functions and optimization in some V8 versions. +### ✅ Best Practice +```javascript +const sum = (...args) => args.reduce((a, b) => a + b); +``` +### 🚀 Solution +Use Rest Parameters (`...args`). They create a real array and are more explicit about the function's intent. + +## 13. Manual array copying vs Spread +**Context:** Immutability and array manipulation. +### ❌ Bad Practice +```javascript +const original = [1, 2, 3]; +const copy = []; +for (let i = 0; i < original.length; i++) { + copy.push(original[i]); +} +``` +### ⚠️ Problem +Manual loops for copying are verbose and imperative. They increase the surface area for bugs (off-by-one errors). +### ✅ Best Practice +```javascript +const original = [1, 2, 3]; +const copy = [...original]; +``` +### 🚀 Solution +Use the Spread Operator (`...`). It is concise, declarative, and highly optimized by modern engines. + +## 14. Nested Destructuring +**Context:** Extracting data from complex objects. +### ❌ Bad Practice +```javascript +const city = user.location.address.city; +const zip = user.location.address.zip; +``` +### ⚠️ Problem +Repetitive property access is verbose and risks "cannot read property of undefined" errors if any parent object is missing. +### ✅ Best Practice +```javascript +const { location: { address: { city, zip } } } = user; +``` +### 🚀 Solution +Use nested destructuring to extract deeply nested values in a single statement. (Note: Combine with optional chaining if path existence isn't guaranteed). + +## 15. Default Parameters +**Context:** Handling missing arguments. +### ❌ Bad Practice +```javascript +function setRole(role) { + role = role || 'guest'; + // ... +} +``` +### ⚠️ Problem +Using `||` for defaults is dangerous if the argument is a "falsy" but valid value (like `0`, `false`, or `''`). +### ✅ Best Practice +```javascript +function setRole(role = 'guest') { + // ... +} +``` +### 🚀 Solution +Use ES6 Default Parameters. They only apply if the argument is `undefined`. + +## 16. `forEach` for data transformation vs `map/filter` +**Context:** Declarative vs Imperative programming. +### ❌ Bad Practice +```javascript +const double = []; +numbers.forEach(n => { + double.push(n * 2); +}); +``` +### ⚠️ Problem +`forEach` relies on side effects (mutating an outer array). It is less expressive and harder to chain than functional alternatives. +### ✅ Best Practice +```javascript +const double = numbers.map(n => n * 2); +``` +### 🚀 Solution +Use `map`, `filter`, and `reduce` for data transformations. They return new arrays and promote immutability. + +## 17. Object mutation vs Immutability +**Context:** State management and predictability. +### ❌ Bad Practice +```javascript +function updateAge(user) { + user.age = 30; // Mutates original object + return user; +} +``` +### ⚠️ Problem +Mutating objects passed by reference can lead to side effects in other parts of the application that share the same reference, making debugging a nightmare. +### ✅ Best Practice +```javascript +const updateAge = (user) => ({ ...user, age: 30 }); +``` +### 🚀 Solution +Treat objects as immutable. Use the spread operator to create copies with updated properties. + +## 18. Switch statements vs Object Literals +**Context:** Simplifying conditional branching. +### ❌ Bad Practice +```javascript +switch (action) { + case 'CREATE': return doCreate(); + case 'UPDATE': return doUpdate(); + default: return doNothing(); +} +``` +### ⚠️ Problem +`switch` statements are verbose, require `break` to prevent fallthrough bugs, and have a non-standard block scope. +### ✅ Best Practice +```javascript +const actions = { + CREATE: doCreate, + UPDATE: doUpdate +}; +return (actions[action] || doNothing)(); +``` +### 🚀 Solution +Use an Object Literal (or Map) as a lookup table. It is cleaner, faster, and more extensible. + +## 19. Not using Optional Chaining `?.` +**Context:** Safe property access in nested objects. +### ❌ Bad Practice +```javascript +const street = user && user.address && user.address.street; +``` +### ⚠️ Problem +The "logical AND" chain is verbose and repetitive. It quickly becomes unreadable with deeper nesting. +### ✅ Best Practice +```javascript +const street = user?.address?.street; +``` +### 🚀 Solution +Use Optional Chaining (`?.`). It short-circuits to `undefined` if any part of the chain is nullish. + +## 20. Not using Nullish Coalescing `??` +**Context:** Providing fallback values safely. +### ❌ Bad Practice +```javascript +const timeout = config.timeout || 5000; +``` +### ⚠️ Problem +If `config.timeout` is `0`, the code will incorrectly fall back to `5000` because `0` is falsy. +### ✅ Best Practice +```javascript +const timeout = config.timeout ?? 5000; +``` +### 🚀 Solution +Use Nullish Coalescing (`??`). It only falls back if the value is `null` or `undefined`, allowing `0`, `false`, and `''` to be valid. + +--- diff --git a/frontend/javascript/professional-niche.md b/frontend/javascript/professional-niche.md new file mode 100644 index 0000000..0e47f41 --- /dev/null +++ b/frontend/javascript/professional-niche.md @@ -0,0 +1,194 @@ +--- +description: Vibe coding guidelines and architectural constraints for JavaScript Professional & Niche topics within the frontend domain. +technology: JavaScript +domain: frontend +level: Senior/Architect +version: "ES2024+" +tags: [javascript, advanced, best-practices, clean-code, scalable-code] +ai_role: Senior JavaScript Expert +last_updated: 2026-03-22 +--- + +# 🧠 JavaScript Professional & Niche Best Practices (Senior Level) + +# 📖 Context & Scope +- **Primary Goal:** Detail advanced and niche topics in JavaScript for high-performance applications. +- **Target Tooling:** Cursor, Windsurf, Antigravity. +- **Tech Stack Version:** ES2024+ + +## IV. Professional & Niche (Senior Level) + +## 31. Memory Leaks: Unremoved Event Listeners +**Context:** Long-lived applications (SPAs). +### ❌ Bad Practice +```javascript +window.addEventListener('resize', () => this.handleResize()); +// Component unmounts, but listener remains +``` +### ⚠️ Problem +The event listener keeps a reference to the component/context, preventing garbage collection even after the component is destroyed. +### ✅ Best Practice +```javascript +const handler = () => this.handleResize(); +window.addEventListener('resize', handler); +// Cleanup: +window.removeEventListener('resize', handler); +``` +### 🚀 Solution +Always remove event listeners in cleanup phases (e.g., `componentWillUnmount` or `useEffect` return). Use `AbortController` for an even more modern approach to listener cleanup. + +## 32. Memory Leaks: Forgotten Intervals/Timeouts +**Context:** Managing temporal background tasks. +### ❌ Bad Practice +```javascript +setInterval(() => { + fetchStatus(); +}, 1000); +``` +### ⚠️ Problem +Intervals continue to run indefinitely until the page is closed, even if the data they process is no longer needed, consuming CPU and memory. +### ✅ Best Practice +```javascript +const intervalId = setInterval(fetchStatus, 1000); +// Later: +clearInterval(intervalId); +``` +### 🚀 Solution +Store the ID returned by `setTimeout` or `setInterval` and clear it when the task is no longer relevant. + +## 33. Closures inside loops (Memory/Scope issues) +**Context:** Understanding the Event Loop and closure capture. +### ❌ Bad Practice +```javascript +for (var i = 0; i < 5; i++) { + setTimeout(() => console.log(i), 100); // Prints '5' five times +} +``` +### ⚠️ Problem +`var` is function-scoped. By the time the `setTimeout` executes, the loop has finished and `i` is 5. Each closure shares the same reference to `i`. +### ✅ Best Practice +```javascript +for (let i = 0; i < 5; i++) { + setTimeout(() => console.log(i), 100); // Prints 0, 1, 2, 3, 4 +} +``` +### 🚀 Solution +Use `let` in loop headers. It creates a new binding for each iteration, ensuring the closure captures the value of `i` at that specific moment. + +## 34. Throwing Strings instead of `new Error()` +**Context:** Ensuring useful stack traces. +### ❌ Bad Practice +```javascript +throw 'Something went wrong'; +``` +### ⚠️ Problem +Throwing a string provides no stack trace. It makes it nearly impossible to determine where the error originated in a complex call stack. +### ✅ Best Practice +```javascript +throw new Error('Something went wrong'); +``` +### 🚀 Solution +Always throw an instance of `Error` (or a subclass). This captures the `stack` property, which is vital for debugging. + +## 35. Modifying Built-in Prototypes +**Context:** Ecosystem compatibility and stability. +### ❌ Bad Practice +```javascript +Array.prototype.last = function() { + return this[this.length - 1]; +}; +``` +### ⚠️ Problem +"Monkey patching" built-ins can lead to collisions if a future ECMAScript version implements a method with the same name but different behavior. It also breaks for-in loops if not handled carefully. +### ✅ Best Practice +```javascript +const last = (arr) => arr[arr.length - 1]; +``` +### 🚀 Solution +Use utility functions or wrapper classes instead of modifying global prototypes. + +## 36. Premature Optimization (e.g., bitwise for rounding) +**Context:** Readability vs Micro-benchmarks. +### ❌ Bad Practice +```javascript +const floor = ~~x; // Double bitwise NOT to floor +``` +### ⚠️ Problem +While `~~` is slightly faster in some engines, it makes the code cryptic. It also only works for numbers within the 32-bit integer range. +### ✅ Best Practice +```javascript +const floor = Math.floor(x); +``` +### 🚀 Solution +Prioritize readability. Modern JIT compilers are smart enough to optimize `Math.floor`. Only use bitwise tricks if profiling proves it's a critical bottleneck in a hot path. + +## 37. V8 Hidden Classes: Changing object shape after initialization +**Context:** V8 JIT optimization. +### ❌ Bad Practice +```javascript +function User(name) { + this.name = name; +} +const u1 = new User('Alice'); +u1.age = 25; // Dynamically adding property +``` +### ⚠️ Problem +V8 creates "Hidden Classes" to optimize object property access. Adding properties after initialization changes the "shape" of the object, causing V8 to drop to a slower "Dictionary Mode" for that object. +### ✅ Best Practice +```javascript +function User(name, age) { + this.name = name; + this.age = age; +} +const u1 = new User('Alice', 25); +``` +### 🚀 Solution +Initialize all object properties in the constructor or a factory function. Maintain a consistent object "shape" to keep V8 in the optimized path. + +## 38. Array Hole (Sparse Arrays) performance +**Context:** Memory allocation and JIT optimization. +### ❌ Bad Practice +```javascript +const arr = new Array(100); +arr[50] = 'val'; +``` +### ⚠️ Problem +Creating "holes" in arrays makes them "sparse". Sparse arrays are stored differently (as hash maps) which is much slower for iteration and access than "packed" arrays. +### ✅ Best Practice +```javascript +const arr = Array.from({ length: 100 }, () => null); +``` +### 🚀 Solution +Initialize arrays with default values (like `null` or `0`) instead of leaving empty slots. This keeps the array in "packed" mode. + +## 39. Using `eval()` or `new Function()` +**Context:** Security and performance. +### ❌ Bad Practice +```javascript +const result = eval('2 + 2'); +``` +### ⚠️ Problem +`eval()` executes strings as code, opening a massive XSS security vulnerability if the string contains user input. It also prevents the JIT compiler from optimizing the surrounding scope. +### ✅ Best Practice +```javascript +const result = new Function('a', 'b', 'return a + b')(2, 2); // Slightly better, but still risky +// Better: +const operations = { '+': (a, b) => a + b }; +``` +### 🚀 Solution +Avoid `eval()`. Use lookup tables, JSON parsing, or safe math libraries to handle dynamic logic. + +## 40. Micro-optimizations that hurt readability +**Context:** Maintaining a healthy codebase. +### ❌ Bad Practice +```javascript +for (let i = 0, len = arr.length; i < len; i++) { /* ... */ } +``` +### ⚠️ Problem +Caching `arr.length` was necessary 15 years ago. Today, modern engines optimize this automatically. Adding extra variables for micro-gains makes the code harder to read. +### ✅ Best Practice +```javascript +for (const item of arr) { /* ... */ } +``` +### 🚀 Solution +Focus on "Big O" complexity and clean code. Only micro-optimize after profiling identifies a specific performance hotspot. diff --git a/frontend/javascript/readme.md b/frontend/javascript/readme.md index 7471a9c..802f4be 100644 --- a/frontend/javascript/readme.md +++ b/frontend/javascript/readme.md @@ -9,7 +9,7 @@ ai_role: Senior JavaScript Performance Expert last_updated: 2026-03-22 --- -# JavaScript Best Practise +# 🎨 JavaScript Best Practise ![JavaScript Logo](https://img.icons8.com/?size=100&id=108784&format=png&color=000000) @@ -223,579 +223,11 @@ Use descriptive, camelCase names that convey the intent and data type of the var --- -## II. Modern Syntax & FP (ES6-ES2024) -## 11. Manual object property assignment vs Shorthand -**Context:** Reducing boilerplate in object creation. -### ❌ Bad Practice -```javascript -const name = 'Alice'; -const user = { - name: name, - age: age -}; -``` -### ⚠️ Problem -Redundant repetition of keys and values increases file size and makes the code noisier. -### ✅ Best Practice -```javascript -const name = 'Alice'; -const user = { name, age }; -``` -### 🚀 Solution -Use Property Shorthand. When the key and variable name match, omit the value. - -## 12. Using `arguments` vs Rest parameters -**Context:** Handling variable numbers of arguments. -### ❌ Bad Practice -```javascript -function sum() { - const args = Array.prototype.slice.call(arguments); - return args.reduce((a, b) => a + b); -} -``` -### ⚠️ Problem -The `arguments` object is not a real array (it lacks methods like `map` or `reduce`). It is also incompatible with arrow functions and optimization in some V8 versions. -### ✅ Best Practice -```javascript -const sum = (...args) => args.reduce((a, b) => a + b); -``` -### 🚀 Solution -Use Rest Parameters (`...args`). They create a real array and are more explicit about the function's intent. - -## 13. Manual array copying vs Spread -**Context:** Immutability and array manipulation. -### ❌ Bad Practice -```javascript -const original = [1, 2, 3]; -const copy = []; -for (let i = 0; i < original.length; i++) { - copy.push(original[i]); -} -``` -### ⚠️ Problem -Manual loops for copying are verbose and imperative. They increase the surface area for bugs (off-by-one errors). -### ✅ Best Practice -```javascript -const original = [1, 2, 3]; -const copy = [...original]; -``` -### 🚀 Solution -Use the Spread Operator (`...`). It is concise, declarative, and highly optimized by modern engines. - -## 14. Nested Destructuring -**Context:** Extracting data from complex objects. -### ❌ Bad Practice -```javascript -const city = user.location.address.city; -const zip = user.location.address.zip; -``` -### ⚠️ Problem -Repetitive property access is verbose and risks "cannot read property of undefined" errors if any parent object is missing. -### ✅ Best Practice -```javascript -const { location: { address: { city, zip } } } = user; -``` -### 🚀 Solution -Use nested destructuring to extract deeply nested values in a single statement. (Note: Combine with optional chaining if path existence isn't guaranteed). - -## 15. Default Parameters -**Context:** Handling missing arguments. -### ❌ Bad Practice -```javascript -function setRole(role) { - role = role || 'guest'; - // ... -} -``` -### ⚠️ Problem -Using `||` for defaults is dangerous if the argument is a "falsy" but valid value (like `0`, `false`, or `''`). -### ✅ Best Practice -```javascript -function setRole(role = 'guest') { - // ... -} -``` -### 🚀 Solution -Use ES6 Default Parameters. They only apply if the argument is `undefined`. - -## 16. `forEach` for data transformation vs `map/filter` -**Context:** Declarative vs Imperative programming. -### ❌ Bad Practice -```javascript -const double = []; -numbers.forEach(n => { - double.push(n * 2); -}); -``` -### ⚠️ Problem -`forEach` relies on side effects (mutating an outer array). It is less expressive and harder to chain than functional alternatives. -### ✅ Best Practice -```javascript -const double = numbers.map(n => n * 2); -``` -### 🚀 Solution -Use `map`, `filter`, and `reduce` for data transformations. They return new arrays and promote immutability. - -## 17. Object mutation vs Immutability -**Context:** State management and predictability. -### ❌ Bad Practice -```javascript -function updateAge(user) { - user.age = 30; // Mutates original object - return user; -} -``` -### ⚠️ Problem -Mutating objects passed by reference can lead to side effects in other parts of the application that share the same reference, making debugging a nightmare. -### ✅ Best Practice -```javascript -const updateAge = (user) => ({ ...user, age: 30 }); -``` -### 🚀 Solution -Treat objects as immutable. Use the spread operator to create copies with updated properties. - -## 18. Switch statements vs Object Literals -**Context:** Simplifying conditional branching. -### ❌ Bad Practice -```javascript -switch (action) { - case 'CREATE': return doCreate(); - case 'UPDATE': return doUpdate(); - default: return doNothing(); -} -``` -### ⚠️ Problem -`switch` statements are verbose, require `break` to prevent fallthrough bugs, and have a non-standard block scope. -### ✅ Best Practice -```javascript -const actions = { - CREATE: doCreate, - UPDATE: doUpdate -}; -return (actions[action] || doNothing)(); -``` -### 🚀 Solution -Use an Object Literal (or Map) as a lookup table. It is cleaner, faster, and more extensible. - -## 19. Not using Optional Chaining `?.` -**Context:** Safe property access in nested objects. -### ❌ Bad Practice -```javascript -const street = user && user.address && user.address.street; -``` -### ⚠️ Problem -The "logical AND" chain is verbose and repetitive. It quickly becomes unreadable with deeper nesting. -### ✅ Best Practice -```javascript -const street = user?.address?.street; -``` -### 🚀 Solution -Use Optional Chaining (`?.`). It short-circuits to `undefined` if any part of the chain is nullish. - -## 20. Not using Nullish Coalescing `??` -**Context:** Providing fallback values safely. -### ❌ Bad Practice -```javascript -const timeout = config.timeout || 5000; -``` -### ⚠️ Problem -If `config.timeout` is `0`, the code will incorrectly fall back to `5000` because `0` is falsy. -### ✅ Best Practice -```javascript -const timeout = config.timeout ?? 5000; -``` -### 🚀 Solution -Use Nullish Coalescing (`??`). It only falls back if the value is `null` or `undefined`, allowing `0`, `false`, and `''` to be valid. - ---- - -## III. Asynchronous & Logic - -## 21. Callback Hell vs Promises -**Context:** Managing asynchronous execution flow. -### ❌ Bad Practice -```javascript -getData(url, (err, res) => { - getDetails(res.id, (err, details) => { - saveData(details, (err, ok) => { - // Callback Hell - }); - }); -}); -``` -### ⚠️ Problem -Deeply nested callbacks (the "Pyramid of Doom") make error handling extremely difficult and code unreadable. -### ✅ Best Practice -```javascript -fetchData(url) - .then(res => fetchDetails(res.id)) - .then(details => saveData(details)) - .catch(err => handleError(err)); -``` -### 🚀 Solution -Use Promises to flatten the structure and centralize error handling with `.catch()`. - -## 22. Promise.then() nesting vs Async/Await -**Context:** Modern syntax for asynchronous code. -### ❌ Bad Practice -```javascript -function load() { - return api.get().then(res => { - return api.process(res).then(processed => { - return processed; - }); - }); -} -``` -### ⚠️ Problem -Even with Promises, `.then()` nesting can occur. It still feels like "callback style" logic. -### ✅ Best Practice -```javascript -async function load() { - const res = await api.get(); - const processed = await api.process(res); - return processed; -} -``` -### 🚀 Solution -Use `async/await`. It allows asynchronous code to be written and read like synchronous code, improving maintainability. +## 📚 Specialized Topics -## 23. Sequential `await` in loops vs `Promise.all` -**Context:** Parallelizing independent asynchronous operations. -### ❌ Bad Practice -```javascript -for (const id of ids) { - await fetchItem(id); // Pauses loop for each request -} -``` -### ⚠️ Problem -Sequential `await` in a loop causes a "waterfall" effect, where each request waits for the previous one to finish, significantly increasing total execution time. -### ✅ Best Practice -```javascript -const promises = ids.map(id => fetchItem(id)); -await Promise.all(promises); -``` -### 🚀 Solution -Use `Promise.all` to execute independent promises in parallel. This utilizes the full network/IO bandwidth. +For further reading, please refer to the following specialized guides: -## 24. Missing `try/catch` in async -**Context:** Handling failures in async functions. -### ❌ Bad Practice -```javascript -async function getData() { - const data = await fetch(url); // If this fails, the process might crash - return data; -} -``` -### ⚠️ Problem -Unhandled exceptions in `async` functions result in unhandled promise rejections, which can lead to silent failures or process termination in Node.js. -### ✅ Best Practice -```javascript -async function getData() { - try { - const data = await fetch(url); - return data; - } catch (error) { - logError(error); - } -} -``` -### 🚀 Solution -Wrap `await` calls in `try/catch` blocks or use a higher-order function to catch errors. - -## 25. Floating point math errors (`0.1 + 0.2`) -**Context:** Precision issues in IEEE 754 arithmetic. -### ❌ Bad Practice -```javascript -if (0.1 + 0.2 === 0.3) { /* False! */ } -``` -### ⚠️ Problem -$\text{0.1} + \text{0.2} = \text{0.30000000000000004}$ due to binary representation limits. This leads to critical bugs in financial or scientific applications. -### ✅ Best Practice -```javascript -const EPSILON = Number.EPSILON; -const areEqual = (a, b) => Math.abs(a - b) < EPSILON; - -// Or for money: -const totalCents = (10 + 20); // 30 cents -``` -### 🚀 Solution -Use `Number.EPSILON` for comparisons or represent decimals as integers (e.g., cents instead of dollars) to avoid floating point drift. - -## 26. Multiple Boolean flags vs State Machine -**Context:** Managing complex component logic. -### ❌ Bad Practice -```javascript -const [isLoading, setIsLoading] = useState(false); -const [isError, setIsError] = useState(false); -const [isSuccess, setIsSuccess] = useState(false); -``` -### ⚠️ Problem -Multiple flags allow for "impossible states" (e.g., `isLoading` and `isError` both being `true`). This makes logic branches exponentially complex. -### ✅ Best Practice -```javascript -const [status, setStatus] = useState('IDLE'); // IDLE, LOADING, ERROR, SUCCESS -``` -### 🚀 Solution -Use a single state variable or a state machine. This ensures only one state is active at a time and simplifies transitions. - -## 27. Sync logic in Event Loop -**Context:** Keeping the UI responsive. -### ❌ Bad Practice -```javascript -function processLargeArray(arr) { - // Blocks the main thread for 2 seconds - arr.sort().forEach(item => complexCalc(item)); -} -``` -### ⚠️ Problem -JavaScript is single-threaded. Heavy synchronous computation blocks the Event Loop, causing the UI to freeze and preventing user interaction. -### ✅ Best Practice -```javascript -// Use Web Workers or break into chunks -function processInChunks(arr) { - if (arr.length === 0) return; - const chunk = arr.splice(0, 100); - process(chunk); - setTimeout(() => processInChunks(arr), 0); -} -``` -### 🚀 Solution -Offload heavy tasks to Web Workers or use `requestIdleCallback`/`setTimeout` to break long tasks into smaller chunks, allowing the browser to render between frames. - -## 28. Overusing `classes` where functions suffice -**Context:** Paradigm choice (OOP vs FP). -### ❌ Bad Practice -```javascript -class Calculator { - add(a, b) { return a + b; } -} -const calc = new Calculator(); -``` -### ⚠️ Problem -Classes introduce unnecessary overhead (prototype chain, `this` binding issues) and make tree-shaking harder for bundlers. -### ✅ Best Practice -```javascript -export const add = (a, b) => a + b; -``` -### 🚀 Solution -Use simple functions and modules for logic. Use classes only when you need to manage complex stateful instances with shared behavior. - -## 29. Hard-coded Error messages vs Error Classes -**Context:** Robust error handling and debugging. -### ❌ Bad Practice -```javascript -throw new Error('User not found'); -``` -### ⚠️ Problem -Parsing error messages in `catch` blocks is brittle. If the string changes, the error handling logic breaks. -### ✅ Best Practice -```javascript -class UserNotFoundError extends Error { - constructor(userId) { - super(`User ${userId} not found`); - this.name = 'UserNotFoundError'; - this.code = 404; - } -} -``` -### 🚀 Solution -Extend the `Error` class to create custom error types. Use `instanceof` check in catch blocks to handle specific errors differently. - -## 30. Unhandled Rejections -**Context:** Reliability of asynchronous flows. -### ❌ Bad Practice -```javascript -// No .catch() or try/catch -fetch('/api/data'); -``` -### ⚠️ Problem -Unhandled rejections create silent failures. In production environments, this can lead to memory leaks as the promise state stays pending or rejected without being cleared. -### ✅ Best Practice -```javascript -window.addEventListener('unhandledrejection', event => { - reportToSentry(event.reason); -}); -``` -### 🚀 Solution -Always handle promise rejections. Implement a global unhandled rejection listener as a safety net for monitoring. - ---- - -## IV. Professional & Niche (Senior Level) - -## 31. Memory Leaks: Unremoved Event Listeners -**Context:** Long-lived applications (SPAs). -### ❌ Bad Practice -```javascript -window.addEventListener('resize', () => this.handleResize()); -// Component unmounts, but listener remains -``` -### ⚠️ Problem -The event listener keeps a reference to the component/context, preventing garbage collection even after the component is destroyed. -### ✅ Best Practice -```javascript -const handler = () => this.handleResize(); -window.addEventListener('resize', handler); -// Cleanup: -window.removeEventListener('resize', handler); -``` -### 🚀 Solution -Always remove event listeners in cleanup phases (e.g., `componentWillUnmount` or `useEffect` return). Use `AbortController` for an even more modern approach to listener cleanup. - -## 32. Memory Leaks: Forgotten Intervals/Timeouts -**Context:** Managing temporal background tasks. -### ❌ Bad Practice -```javascript -setInterval(() => { - fetchStatus(); -}, 1000); -``` -### ⚠️ Problem -Intervals continue to run indefinitely until the page is closed, even if the data they process is no longer needed, consuming CPU and memory. -### ✅ Best Practice -```javascript -const intervalId = setInterval(fetchStatus, 1000); -// Later: -clearInterval(intervalId); -``` -### 🚀 Solution -Store the ID returned by `setTimeout` or `setInterval` and clear it when the task is no longer relevant. - -## 33. Closures inside loops (Memory/Scope issues) -**Context:** Understanding the Event Loop and closure capture. -### ❌ Bad Practice -```javascript -for (var i = 0; i < 5; i++) { - setTimeout(() => console.log(i), 100); // Prints '5' five times -} -``` -### ⚠️ Problem -`var` is function-scoped. By the time the `setTimeout` executes, the loop has finished and `i` is 5. Each closure shares the same reference to `i`. -### ✅ Best Practice -```javascript -for (let i = 0; i < 5; i++) { - setTimeout(() => console.log(i), 100); // Prints 0, 1, 2, 3, 4 -} -``` -### 🚀 Solution -Use `let` in loop headers. It creates a new binding for each iteration, ensuring the closure captures the value of `i` at that specific moment. - -## 34. Throwing Strings instead of `new Error()` -**Context:** Ensuring useful stack traces. -### ❌ Bad Practice -```javascript -throw 'Something went wrong'; -``` -### ⚠️ Problem -Throwing a string provides no stack trace. It makes it nearly impossible to determine where the error originated in a complex call stack. -### ✅ Best Practice -```javascript -throw new Error('Something went wrong'); -``` -### 🚀 Solution -Always throw an instance of `Error` (or a subclass). This captures the `stack` property, which is vital for debugging. - -## 35. Modifying Built-in Prototypes -**Context:** Ecosystem compatibility and stability. -### ❌ Bad Practice -```javascript -Array.prototype.last = function() { - return this[this.length - 1]; -}; -``` -### ⚠️ Problem -"Monkey patching" built-ins can lead to collisions if a future ECMAScript version implements a method with the same name but different behavior. It also breaks for-in loops if not handled carefully. -### ✅ Best Practice -```javascript -const last = (arr) => arr[arr.length - 1]; -``` -### 🚀 Solution -Use utility functions or wrapper classes instead of modifying global prototypes. - -## 36. Premature Optimization (e.g., bitwise for rounding) -**Context:** Readability vs Micro-benchmarks. -### ❌ Bad Practice -```javascript -const floor = ~~x; // Double bitwise NOT to floor -``` -### ⚠️ Problem -While `~~` is slightly faster in some engines, it makes the code cryptic. It also only works for numbers within the 32-bit integer range. -### ✅ Best Practice -```javascript -const floor = Math.floor(x); -``` -### 🚀 Solution -Prioritize readability. Modern JIT compilers are smart enough to optimize `Math.floor`. Only use bitwise tricks if profiling proves it's a critical bottleneck in a hot path. - -## 37. V8 Hidden Classes: Changing object shape after initialization -**Context:** V8 JIT optimization. -### ❌ Bad Practice -```javascript -function User(name) { - this.name = name; -} -const u1 = new User('Alice'); -u1.age = 25; // Dynamically adding property -``` -### ⚠️ Problem -V8 creates "Hidden Classes" to optimize object property access. Adding properties after initialization changes the "shape" of the object, causing V8 to drop to a slower "Dictionary Mode" for that object. -### ✅ Best Practice -```javascript -function User(name, age) { - this.name = name; - this.age = age; -} -const u1 = new User('Alice', 25); -``` -### 🚀 Solution -Initialize all object properties in the constructor or a factory function. Maintain a consistent object "shape" to keep V8 in the optimized path. - -## 38. Array Hole (Sparse Arrays) performance -**Context:** Memory allocation and JIT optimization. -### ❌ Bad Practice -```javascript -const arr = new Array(100); -arr[50] = 'val'; -``` -### ⚠️ Problem -Creating "holes" in arrays makes them "sparse". Sparse arrays are stored differently (as hash maps) which is much slower for iteration and access than "packed" arrays. -### ✅ Best Practice -```javascript -const arr = Array.from({ length: 100 }, () => null); -``` -### 🚀 Solution -Initialize arrays with default values (like `null` or `0`) instead of leaving empty slots. This keeps the array in "packed" mode. - -## 39. Using `eval()` or `new Function()` -**Context:** Security and performance. -### ❌ Bad Practice -```javascript -const result = eval('2 + 2'); -``` -### ⚠️ Problem -`eval()` executes strings as code, opening a massive XSS security vulnerability if the string contains user input. It also prevents the JIT compiler from optimizing the surrounding scope. -### ✅ Best Practice -```javascript -const result = new Function('a', 'b', 'return a + b')(2, 2); // Slightly better, but still risky -// Better: -const operations = { '+': (a, b) => a + b }; -``` -### 🚀 Solution -Avoid `eval()`. Use lookup tables, JSON parsing, or safe math libraries to handle dynamic logic. - -## 40. Micro-optimizations that hurt readability -**Context:** Maintaining a healthy codebase. -### ❌ Bad Practice -```javascript -for (let i = 0, len = arr.length; i < len; i++) { /* ... */ } -``` -### ⚠️ Problem -Caching `arr.length` was necessary 15 years ago. Today, modern engines optimize this automatically. Adding extra variables for micro-gains makes the code harder to read. -### ✅ Best Practice -```javascript -for (const item of arr) { /* ... */ } -``` -### 🚀 Solution -Focus on "Big O" complexity and clean code. Only micro-optimize after profiling identifies a specific performance hotspot. +- [✨ Modern Syntax & Functional Programming](./modern-syntax.md) +- [⏳ Asynchronous & Logic](./async-logic.md) +- [🧠 Professional & Niche Topics](./professional-niche.md) diff --git a/frontend/qwik/readme.md b/frontend/qwik/readme.md new file mode 100644 index 0000000..883ca99 --- /dev/null +++ b/frontend/qwik/readme.md @@ -0,0 +1,47 @@ +--- +description: Vibe coding guidelines and architectural constraints for Qwik within the frontend domain. +technology: Qwik +domain: frontend +level: Senior/Architect +version: "1.x" +tags: [best-practices, clean-code, architecture-patterns, vibe-coding, cursor-rules, typescript, software-architecture, system-design, solid-principles, production-ready, programming-standards, react-best-practices, node-js, design-patterns, scalable-code, windsurf-rules, ai-coding, fsd, ddd, enterprise-patterns] +ai_role: Senior Qwik Expert +last_updated: 2026-03-22 +--- + +# ⚡ Qwik Best Practices & Production-Ready Patterns + +# 📖 Context & Scope +- **Primary Goal:** Enforce strict adherence to modern Qwik patterns, specifically resumability and lazy loading for optimal best practices. +- **Target Tooling:** Cursor, Windsurf, Antigravity. +- **Tech Stack Version:** Qwik 1.x + +> [!IMPORTANT] +> **Strict Constraints for AI:** +> - **Always** use `useSignal()`, `useStore()`, and `useTask$()` for local state and effects. +> - **Never** pass non-serializable objects (like closures, Promises, or DOM references) into generic properties. +> - **Always** utilize the `$` suffix for closures when necessary, like `onClick$`, to indicate lazy loading points. + +## 🏗 Architecture Principles + +- Adhere to the defined [Architectural Patterns](../../architectures/readme.md) when building applications. +- Strongly prefer **Feature Sliced Design (FSD)** for applications scaling across multiple teams. + +## 🚀 I. Basics & Popular + +## 1. Passing Closures as Props +**Context:** Component Props +### ❌ Bad Practice +```tsx +const Component = ({ onClick }) => ; +``` +### ⚠️ Problem +Closures cannot be serialized natively by Qwik, breaking resumability and throwing an error. +### ✅ Best Practice +```tsx +const Component = component$(({ onClick$ }: { onClick$: PropFunction<() => void> }) => ( + +)); +``` +### 🚀 Solution +Use the `$` suffix (`onClick$`) to mark the prop as a `PropFunction`, allowing Qwik to serialize the closure and load it lazily. diff --git a/frontend/readme.md b/frontend/readme.md index d28bf91..5340ebc 100644 --- a/frontend/readme.md +++ b/frontend/readme.md @@ -9,9 +9,9 @@ ai_role: Senior Frontend Architect last_updated: 2026-03-22 --- -# Frontend Best Practices & Production-Ready Patterns +# 🎨 Frontend Best Practices & Production-Ready Patterns -# Context & Scope +# 📖 Context & Scope - **Primary Goal:** Outline the overarching philosophy and standards for Frontend development inside the ecosystem. - **Target Tooling:** Cursor, Windsurf, Antigravity. - **Tech Stack Version:** Agnostic @@ -24,12 +24,12 @@ last_updated: 2026-03-22 --- -## Architecture Principles +## 🏗 Architecture Principles - Adhere to the defined [Architectural Patterns](../../architectures/readme.md) when building applications. - Strongly prefer **Feature Sliced Design (FSD)** for applications scaling across multiple teams. -## Technical Requirements for AI Generation +## 🤖 Technical Requirements for AI Generation > [!IMPORTANT] > **Constraint:** Do not mutate shared DOM properties directly unless explicitly interacting with Browser APIs outside the framework context. @@ -38,9 +38,11 @@ last_updated: 2026-03-22 - **TypeScript Strictness:** Exploit TypeScript. `any` is strictly prohibited. Use explicit return types for all public methods. - **State Management:** Abstract global state logically depending on the specific framework rules, but never tightly couple presentation layers directly to store calls. -## Technologies Included +## 💻 Technologies Included This folder acts as a container for documentation around the following technologies: - [Angular](./angular/readme.md) - [JavaScript](./javascript/readme.md) - [TypeScript](./typescript/readme.md) +- [SolidJS](./solidjs/readme.md) +- [Qwik](./qwik/readme.md) diff --git a/frontend/solidjs/readme.md b/frontend/solidjs/readme.md new file mode 100644 index 0000000..fa1a2cd --- /dev/null +++ b/frontend/solidjs/readme.md @@ -0,0 +1,45 @@ +--- +description: Vibe coding guidelines and architectural constraints for SolidJS within the frontend domain. +technology: SolidJS +domain: frontend +level: Senior/Architect +version: "1.8+" +tags: [best-practices, clean-code, architecture-patterns, vibe-coding, cursor-rules, typescript, software-architecture, system-design, solid-principles, production-ready, programming-standards, react-best-practices, node-js, design-patterns, scalable-code, windsurf-rules, ai-coding, fsd, ddd, enterprise-patterns] +ai_role: Senior SolidJS Expert +last_updated: 2026-03-22 +--- + +# ⚡ SolidJS Best Practices & Production-Ready Patterns + +# 📖 Context & Scope +- **Primary Goal:** Enforce strict adherence to modern SolidJS patterns, specifically fine-grained reactivity and functional APIs for optimal best practices. +- **Target Tooling:** Cursor, Windsurf, Antigravity. +- **Tech Stack Version:** SolidJS 1.8+ + +> [!IMPORTANT] +> **Strict Constraints for AI:** +> - **Always** use `createSignal()`, `createMemo()`, and `createEffect()` for local state and side effects. +> - **Never** destructure props directly; use `splitProps()` or `mergeProps()` instead. +> - **Always** utilize the built-in control flow (``, ``, ``) instead of mapping or ternary operators in JSX. + +## 🏗 Architecture Principles + +- Adhere to the defined [Architectural Patterns](../../architectures/readme.md) when building applications. +- Strongly prefer **Feature Sliced Design (FSD)** for applications scaling across multiple teams. + +## 🚀 I. Basics & Popular + +## 1. Using JSX Map for Lists +**Context:** Rendering Lists +### ❌ Bad Practice +```tsx +return
      {items().map(item =>
    • {item.name}
    • )}
    ; +``` +### ⚠️ Problem +Using `.map` creates the DOM nodes once and does not react to array changes optimally, leading to unnecessary re-renders or lost reactivity. +### ✅ Best Practice +```tsx +return
      {item =>
    • {item.name}
    • }
    ; +``` +### 🚀 Solution +Use the `` component. It caches DOM elements and handles granular updates when the array changes. diff --git a/frontend/typescript/logic-safety.md b/frontend/typescript/logic-safety.md new file mode 100644 index 0000000..3dee318 --- /dev/null +++ b/frontend/typescript/logic-safety.md @@ -0,0 +1,204 @@ +--- +description: Vibe coding guidelines and architectural constraints for TypeScript Logic & Safety within the frontend domain. +technology: TypeScript +domain: frontend +level: Senior/Architect +version: "5.5+" +tags: [typescript, type-safety, best-practices, clean-code, scalable-code] +ai_role: Senior TypeScript Expert +last_updated: 2026-03-22 +--- + +# 🛡️ TypeScript Logic & Safety Best Practices + +# 📖 Context & Scope +- **Primary Goal:** Enforce strict type safety and logical soundness in TypeScript. +- **Target Tooling:** Cursor, Windsurf, Antigravity. +- **Tech Stack Version:** TypeScript 5.5+ + +## II. Logic & Safety (11-20) + +## 11. Type Assertions (`as`) vs Narrowing +**Context:** Telling the compiler what a type is. +### ❌ Bad Practice +```typescript +const user = response.data as User; +console.log(user.id); +``` +### ⚠️ Problem +`as` forces the compiler to trust you. If the runtime data doesn't match the interface, the app will crash silently. +### ✅ Best Practice +```typescript +const user = UserSchema.parse(response.data); // Using Zod for runtime validation +// OR +if (isValidUser(response.data)) { ... } +``` +### 🚀 Solution +Avoid type assertions. Use runtime validation (Zod, Valibot) or Type Guards to ensure the data actually matches the type you expect. + +## 12. Non-null Assertion Operator (`!`) +**Context:** Dealing with potentially `null` or `undefined` values. +### ❌ Bad Practice +```typescript +const name = user!.profile!.name; +``` +### ⚠️ Problem +The `!` operator suppresses the compiler warning but doesn't handle the runtime reality. If `user` is null, this throws a `TypeError`. +### ✅ Best Practice +```typescript +const name = user?.profile?.name ?? 'Guest'; +``` +### 🚀 Solution +Use Optional Chaining (`?.`) and Nullish Coalescing (`??`) to handle missing values gracefully. + +## 13. Lack of Discriminated Unions +**Context:** Modeling complex states like API responses. +### ❌ Bad Practice +```typescript +interface State { + isLoading: boolean; + data?: string; + error?: string; +} +``` +### ⚠️ Problem +This allows "impossible states" (e.g., `isLoading: true` AND `data: '...'`). It requires awkward optional checking. +### ✅ Best Practice +```typescript +type State = + | { type: 'LOADING' } + | { type: 'SUCCESS', data: string } + | { type: 'ERROR', error: string }; +``` +### 🚀 Solution +Use Discriminated Unions (with a shared literal property like `type` or `kind`). This makes states mutually exclusive and simplifies logic. + +## 14. Boolean casting (`!!`) +**Context:** Converting values to booleans. +### ❌ Bad Practice +```typescript +const hasAccess = !!user.token; +``` +### ⚠️ Problem +`!!` is cryptic and less readable for beginners. It also doesn't provide type safety if the underlying value could be complex. +### ✅ Best Practice +```typescript +const hasAccess = Boolean(user.token); +// OR +const hasAccess = user.token !== undefined; +``` +### 🚀 Solution +Use the `Boolean()` constructor or explicit comparisons for clarity. + +## 15. Using `Object` for non-primitive types +**Context:** Restricting types to objects. +### ❌ Bad Practice +```typescript +function cache(obj: Object) { ... } +``` +### ⚠️ Problem +The `Object` type (capital O) includes primitives like `string` or `number` because they have methods. `object` (lowercase) is also vague. +### ✅ Best Practice +```typescript +function cache(obj: Record) { ... } +``` +### 🚀 Solution +Use `Record` for general objects or `Record` for empty objects to ensure keys are strings and values are handled safely. + +## 16. Function types vs Object types for functions +**Context:** Defining function signatures. +### ❌ Bad Practice +```typescript +type ClickHandler = { + (e: Event): void; +}; +``` +### ⚠️ Problem +Using the object literal syntax for single functions is unnecessarily complex and harder to read. +### ✅ Best Practice +```typescript +type ClickHandler = (e: Event) => void; +``` +### 🚀 Solution +Use the arrow function syntax for type aliases unless you need to define properties on the function itself (callable objects). + +## 17. Catching `any` in try-catch +**Context:** Handling exceptions. +### ❌ Bad Practice +```typescript +try { + doWork(); +} catch (e) { + console.error(e.message); // e is any by default +} +``` +### ⚠️ Problem +In JavaScript, anything can be thrown (`throw "error"`). Accessing `.message` on a string or null will crash. +### ✅ Best Practice +```typescript +try { + doWork(); +} catch (e) { + if (e instanceof Error) { + console.error(e.message); + } +} +``` +### 🚀 Solution +Always check the type of the caught error. In modern TS, use `useUnknownInCatchVariables: true` to force `e` to be `unknown`. + +## 18. Literal types vs General types +**Context:** Narrowing strings/numbers to specific values. +### ❌ Bad Practice +```typescript +function setAlignment(dir: string) { ... } +``` +### ⚠️ Problem +Accepting any `string` allows invalid inputs like `"center-left"` which the code won't handle. +### ✅ Best Practice +```typescript +type Direction = 'left' | 'right' | 'center'; +function setAlignment(dir: Direction) { ... } +``` +### 🚀 Solution +Use Union Literal types to restrict inputs to a known set of valid values. + +## 19. Optional properties vs Union with `undefined` +**Context:** Defining fields that might not exist. +### ❌ Bad Practice +```typescript +interface Config { + port: number | undefined; +} +``` +### ⚠️ Problem +This requires the key `port` to be present, even if its value is `undefined`. +### ✅ Best Practice +```typescript +interface Config { + port?: number; +} +``` +### 🚀 Solution +Use `?` for properties that can be omitted entirely. + +## 20. Array index access safety +**Context:** Accessing elements by index. +### ❌ Bad Practice +```typescript +const first = users[0]; +console.log(first.id); // Potential crash if array is empty +``` +### ⚠️ Problem +TypeScript assumes `users[0]` always exists if the array type is `User[]`. +### ✅ Best Practice +```typescript +const first = users[0]; +if (first) { + console.log(first.id); +} +``` +### 🚀 Solution +Enable `noUncheckedIndexedAccess: true` in `tsconfig.json`. This forces index access to return `T | undefined`. + +--- diff --git a/frontend/typescript/objects-functions.md b/frontend/typescript/objects-functions.md new file mode 100644 index 0000000..597794a --- /dev/null +++ b/frontend/typescript/objects-functions.md @@ -0,0 +1,202 @@ +--- +description: Vibe coding guidelines and architectural constraints for TypeScript Objects & Functions within the frontend domain. +technology: TypeScript +domain: frontend +level: Senior/Architect +version: "5.5+" +tags: [typescript, objects, functions, best-practices, clean-code, scalable-code] +ai_role: Senior TypeScript Expert +last_updated: 2026-03-22 +--- + +# 📦 TypeScript Objects & Functions Best Practices + +# 📖 Context & Scope +- **Primary Goal:** Proper typing for objects, arrays, and functions in TypeScript applications. +- **Target Tooling:** Cursor, Windsurf, Antigravity. +- **Tech Stack Version:** TypeScript 5.5+ + +## III. Objects & Functions (21-30) + +## 21. Object literals vs `Record` +**Context:** Defining maps/dictionaries. +### ❌ Bad Practice +```typescript +const prices: { [key: string]: number } = { apple: 1 }; +``` +### ⚠️ Problem +The index signature syntax is more verbose and harder to read. +### ✅ Best Practice +```typescript +const prices: Record = { apple: 1 }; +``` +### 🚀 Solution +Use the `Record` utility type for key-value maps. + +## 22. Excess property checks and object spreading +**Context:** Passing objects to functions. +### ❌ Bad Practice +```typescript +const extra = { id: 1, name: 'A', extra: true }; +saveUser({ id: 1, name: 'A', extra: true }); // Error: excess property +saveUser(extra); // No error, but 'extra' is leaked into db +``` +### ⚠️ Problem +Excess property checks only happen on object literals. Spreading or passing variables can bypass this, leading to data pollution. +### ✅ Best Practice +```typescript +const { extra, ...validUser } = data; +saveUser(validUser); +``` +### 🚀 Solution +Be explicit about what data you pass. Use destructuring to strip unknown properties before passing objects to storage or APIs. + +## 23. `Readonly` for Immutability +**Context:** Preventing accidental state mutation. +### ❌ Bad Practice +```typescript +function process(config: Config) { + config.port = 80; // Side effect! +} +``` +### ⚠️ Problem +Mutable inputs lead to unpredictable state changes and bugs that are hard to trace in large applications. +### ✅ Best Practice +```typescript +function process(config: Readonly) { + // config.port = 80; // Error +} +``` +### 🚀 Solution +Use `Readonly` for function parameters and `as const` for configuration objects to enforce immutability at compile time. + +## 24. `Awaited` for Promise Unwrapping +**Context:** Getting the resolved type of a Promise. +### ❌ Bad Practice +```typescript +type Result = typeof apiCall extends Promise ? U : never; +``` +### ⚠️ Problem +Manually unwrapping promises via conditional types is complex and doesn't handle nested promises. +### ✅ Best Practice +```typescript +type Result = Awaited>; +``` +### 🚀 Solution +Use the `Awaited` utility type (TS 4.5+) for clean promise unwrapping. + +## 25. `this` typing in functions +**Context:** Ensuring correct context in callback-heavy code. +### ❌ Bad Practice +```typescript +function handleClick(this: any, event: Event) { + this.classList.add('active'); +} +``` +### ⚠️ Problem +`this` defaults to `any`, making it easy to access properties that don't exist on the actual execution context. +### ✅ Best Practice +```typescript +function handleClick(this: HTMLElement, event: Event) { + this.classList.add('active'); +} +``` +### 🚀 Solution +Always type the first "fake" `this` parameter in functions that rely on a specific execution context. + +## 26. Constructor Shorthand +**Context:** Defining class properties. +### ❌ Bad Practice +```typescript +class User { + public name: string; + constructor(name: string) { + this.name = name; + } +} +``` +### ⚠️ Problem +Redundant repetition of property names in declaration, parameter, and assignment. +### ✅ Best Practice +```typescript +class User { + constructor(public readonly name: string) {} +} +``` +### 🚀 Solution +Use parameter properties in constructors to declare and initialize class members in one step. + +## 27. Abstract classes vs Interfaces +**Context:** Defining blueprints for classes. +### ❌ Bad Practice +```typescript +class BaseService { + getData() { throw new Error("Not implemented"); } +} +``` +### ⚠️ Problem +Normal classes don't force implementation, leading to runtime errors. Interfaces don't allow shared logic. +### ✅ Best Practice +```typescript +abstract class BaseService { + abstract getData(): Promise; + log(msg: string) { console.log(msg); } +} +``` +### 🚀 Solution +Use `abstract` classes when you need to provide shared logic while forcing sub-classes to implement specific methods. + +## 28. Private vs `#private` +**Context:** Encapsulating data in classes. +### ❌ Bad Practice +```typescript +class User { + private secret = 123; +} +console.log(user['secret']); // Works at runtime +``` +### ⚠️ Problem +TypeScript's `private` keyword is only for compile-time. At runtime, the property is fully accessible. +### ✅ Best Practice +```typescript +class User { + #secret = 123; +} +``` +### 🚀 Solution +Use ES2020 `#private` fields for true runtime encapsulation if you are building libraries or high-security components. + +## 29. Decorators (Legacy vs TC39) +**Context:** Meta-programming in TypeScript. +### ❌ Bad Practice +```typescript +// Using experimentalDecorators: true +@Logged +class MyClass {} +``` +### ⚠️ Problem +Legacy decorators are non-standard and might break in future versions of Node/Browsers. +### ✅ Best Practice +Use the new TC39 Decorators (TS 5.0+) which align with the official JavaScript proposal. +### 🚀 Solution +If starting a new project, avoid decorators unless using a framework that mandates them (like NestJS or Angular). + +## 30. Utility Types (`Omit`, `Pick`, `Partial`) +**Context:** Transforming existing types. +### ❌ Bad Practice +```typescript +interface UserUpdate { + name?: string; + age?: number; +} +``` +### ⚠️ Problem +Manual re-declaration of properties leads to synchronization issues when the base `User` interface changes. +### ✅ Best Practice +```typescript +type UserUpdate = Partial>; +``` +### 🚀 Solution +Always derive sub-types from the source of truth using built-in utility types. + +--- diff --git a/frontend/typescript/professional-niche.md b/frontend/typescript/professional-niche.md new file mode 100644 index 0000000..1f46c77 --- /dev/null +++ b/frontend/typescript/professional-niche.md @@ -0,0 +1,218 @@ +--- +description: Vibe coding guidelines and architectural constraints for TypeScript Professional & Niche topics within the frontend domain. +technology: TypeScript +domain: frontend +level: Senior/Architect +version: "5.5+" +tags: [typescript, advanced, best-practices, clean-code, scalable-code] +ai_role: Senior TypeScript Expert +last_updated: 2026-03-22 +--- + +# 🧠 TypeScript Professional & Niche Best Practices + +# 📖 Context & Scope +- **Primary Goal:** Advanced TypeScript features, metaprogramming, and precise utility types. +- **Target Tooling:** Cursor, Windsurf, Antigravity. +- **Tech Stack Version:** TypeScript 5.5+ + +## IV. Professional & Niche (31-40) + +## 31. Using `satisfies` to preserve literal types +**Context:** Checking an object against a type without widening it. +### ❌ Bad Practice +```typescript +const config: Record = { + host: 'localhost' +}; +// config.host is type 'string', not 'localhost' +``` +### ⚠️ Problem +Direct type annotation widens properties to the most general type, losing specific literal information needed for inference. +### ✅ Best Practice +```typescript +const config = { + host: 'localhost' +} satisfies Record; +// config.host is type 'localhost' +``` +### 🚀 Solution +Use `satisfies` (TS 4.9+). It validates the structure but preserves the narrowest possible type for the value. + +## 32. `const` type parameters (TS 5.0) +**Context:** Improving inference for generic constants. +### ❌ Bad Practice +```typescript +function route(paths: T) { ... } +route(['/home', '/about']); // T is string[] +``` +### ⚠️ Problem +Generic inference often widens string arrays to `string[]` unless the caller adds `as const`. +### ✅ Best Practice +```typescript +function route(paths: T) { ... } +route(['/home', '/about']); // T is readonly ['/home', '/about'] +``` +### 🚀 Solution +Use `const` before a type parameter to force the compiler to treat the input as a constant, preserving literal types without requiring the caller to use `as const`. + +## 33. Branding/Tagging for Nominal Typing +**Context:** Preventing accidental mixing of identical primitive types (e.g., `UserId` and `OrderId`). +### ❌ Bad Practice +```typescript +type UserId = string; +type OrderId = string; + +const ship = (u: UserId, o: OrderId) => {}; +ship('order_123', 'user_456'); // No error, but logic is wrong! +``` +### ⚠️ Problem +TypeScript is structural. Two type aliases of `string` are identical and interchangeable. +### ✅ Best Practice +```typescript +type Brand = K & { __brand: T }; +type UserId = Brand; +type OrderId = Brand; + +// Usage requires a cast at creation, but provides safety after +const uid = 'user_1' as UserId; +``` +### 🚀 Solution +Use "Branding" (adding a phantom property) to simulate nominal typing for critical identifiers. + +## 34. Covariance/Contravariance in callbacks +**Context:** Ensuring safe function assignments. +### ❌ Bad Practice +```typescript +interface Logger { + log: (msg: string) => void; // Defined as a property +} +``` +### ⚠️ Problem +Function properties are checked **bivariantly**, which is less strict and can lead to runtime errors when assigning functions with more specialized arguments. +### ✅ Best Practice +```typescript +interface Logger { + log(msg: string): void; // Defined as a method +} +``` +### 🚀 Solution +Use method syntax in interfaces for stricter **contravariant** checking of parameters. + +## 35. Avoiding "God Objects" through Mapped Types +**Context:** Transforming object structures dynamically. +### ❌ Bad Practice +```typescript +interface API { + getUser: () => void; + getPost: () => void; +} +``` +### ⚠️ Problem +Manually adding properties to large interfaces is repetitive and error-prone. +### ✅ Best Practice +```typescript +type Resource = 'User' | 'Post'; +type API = { + [K in Resource as `get${K}`]: () => void; +}; +``` +### 🚀 Solution +Use Mapped Types and Key Remapping (`as`) to generate interface structures from a single source of truth (like a union of keys). + +## 36. Template Literal Types for string-based APIs +**Context:** Enforcing patterns in strings. +### ❌ Bad Practice +```typescript +function setPadding(value: string) { ... } // Accepts "10" (invalid) +``` +### ⚠️ Problem +Strings used for CSS or IDs often have strict patterns that `string` doesn't capture. +### ✅ Best Practice +```typescript +type CssValue = `${number}${'px' | 'em' | 'rem'}`; +function setPadding(value: CssValue) { ... } +``` +### 🚀 Solution +Use Template Literal types to enforce specific string patterns at compile time. + +## 37. Exhaustiveness checking with `never` +**Context:** Ensuring all cases in a union are handled. +### ❌ Bad Practice +```typescript +function handle(action: 'START' | 'STOP') { + switch (action) { + case 'START': return 1; + case 'STOP': return 0; + } +} +``` +### ⚠️ Problem +If you add `'PAUSE'` to the union, the compiler won't warn you that `handle` is now missing a case. +### ✅ Best Practice +```typescript +function handle(action: 'START' | 'STOP' | 'PAUSE') { + switch (action) { + case 'START': return 1; + case 'STOP': return 0; + default: { + const _exhaustive: never = action; // Error: 'PAUSE' is not assignable to never + return _exhaustive; + } + } +} +``` +### 🚀 Solution +Assign the `default` case to a variable of type `never`. This triggers a compile error if any member of the union is unhandled. + +## 38. Recursive Type Aliases +**Context:** Modeling nested structures like JSON or file trees. +### ❌ Bad Practice +```typescript +type Json = string | number | boolean | JsonObject | JsonArray; +interface JsonObject { [key: string]: Json; } +``` +### ⚠️ Problem +Older TS versions required interfaces for recursion. Modern TS allows direct recursion. +### ✅ Best Practice +```typescript +type JSONValue = + | string | number | boolean | null + | { [key: string]: JSONValue } + | JSONValue[]; +``` +### 🚀 Solution +Use recursive type aliases for cleaner definitions of deeply nested data structures. + +## 39. `infer` keyword in conditional types +**Context:** Extracting internal types from complex structures. +### ❌ Bad Practice +```typescript +// Hardcoded extraction +type GetArrayType = T extends string[] ? string : never; +``` +### ⚠️ Problem +Hardcoding extractions limits reusability and doesn't scale. +### ✅ Best Practice +```typescript +type GetArrayType = T extends (infer U)[] ? U : never; +``` +### 🚀 Solution +Use `infer` within conditional types to let TypeScript dynamically capture and name types from within generics. + +## 40. Tuple types for fixed-length data +**Context:** Representing arrays with specific structures (e.g., coordinates). +### ❌ Bad Practice +```typescript +const point: number[] = [10, 20]; +const [x, y, z] = point; // z is undefined, but TS doesn't know +``` +### ⚠️ Problem +`number[]` doesn't capture the length, leading to potential out-of-bounds errors during destructuring. +### ✅ Best Practice +```typescript +const point: [number, number] = [10, 20]; +// const [x, y, z] = point; // Error: Tuple has 2 elements +``` +### 🚀 Solution +Use Tuples for arrays where the number of elements and their positions are fixed and meaningful. diff --git a/frontend/typescript/readme.md b/frontend/typescript/readme.md index ef6f948..510f1aa 100644 --- a/frontend/typescript/readme.md +++ b/frontend/typescript/readme.md @@ -9,7 +9,7 @@ ai_role: Senior TypeScript Architecture Expert last_updated: 2026-03-22 --- -# TypeScript Best Practise +# 🎨 TypeScript Best Practise ![TypeScript Logo](https://img.icons8.com/?size=100&id=uJM6fQYqDaZK&format=png&color=000000) @@ -211,575 +211,11 @@ Use standard ES `import` statements. Manage global types via `tsconfig.json` `ty --- -## II. Logic & Safety (11-20) -## 11. Type Assertions (`as`) vs Narrowing -**Context:** Telling the compiler what a type is. -### ❌ Bad Practice -```typescript -const user = response.data as User; -console.log(user.id); -``` -### ⚠️ Problem -`as` forces the compiler to trust you. If the runtime data doesn't match the interface, the app will crash silently. -### ✅ Best Practice -```typescript -const user = UserSchema.parse(response.data); // Using Zod for runtime validation -// OR -if (isValidUser(response.data)) { ... } -``` -### 🚀 Solution -Avoid type assertions. Use runtime validation (Zod, Valibot) or Type Guards to ensure the data actually matches the type you expect. - -## 12. Non-null Assertion Operator (`!`) -**Context:** Dealing with potentially `null` or `undefined` values. -### ❌ Bad Practice -```typescript -const name = user!.profile!.name; -``` -### ⚠️ Problem -The `!` operator suppresses the compiler warning but doesn't handle the runtime reality. If `user` is null, this throws a `TypeError`. -### ✅ Best Practice -```typescript -const name = user?.profile?.name ?? 'Guest'; -``` -### 🚀 Solution -Use Optional Chaining (`?.`) and Nullish Coalescing (`??`) to handle missing values gracefully. - -## 13. Lack of Discriminated Unions -**Context:** Modeling complex states like API responses. -### ❌ Bad Practice -```typescript -interface State { - isLoading: boolean; - data?: string; - error?: string; -} -``` -### ⚠️ Problem -This allows "impossible states" (e.g., `isLoading: true` AND `data: '...'`). It requires awkward optional checking. -### ✅ Best Practice -```typescript -type State = - | { type: 'LOADING' } - | { type: 'SUCCESS', data: string } - | { type: 'ERROR', error: string }; -``` -### 🚀 Solution -Use Discriminated Unions (with a shared literal property like `type` or `kind`). This makes states mutually exclusive and simplifies logic. - -## 14. Boolean casting (`!!`) -**Context:** Converting values to booleans. -### ❌ Bad Practice -```typescript -const hasAccess = !!user.token; -``` -### ⚠️ Problem -`!!` is cryptic and less readable for beginners. It also doesn't provide type safety if the underlying value could be complex. -### ✅ Best Practice -```typescript -const hasAccess = Boolean(user.token); -// OR -const hasAccess = user.token !== undefined; -``` -### 🚀 Solution -Use the `Boolean()` constructor or explicit comparisons for clarity. - -## 15. Using `Object` for non-primitive types -**Context:** Restricting types to objects. -### ❌ Bad Practice -```typescript -function cache(obj: Object) { ... } -``` -### ⚠️ Problem -The `Object` type (capital O) includes primitives like `string` or `number` because they have methods. `object` (lowercase) is also vague. -### ✅ Best Practice -```typescript -function cache(obj: Record) { ... } -``` -### 🚀 Solution -Use `Record` for general objects or `Record` for empty objects to ensure keys are strings and values are handled safely. - -## 16. Function types vs Object types for functions -**Context:** Defining function signatures. -### ❌ Bad Practice -```typescript -type ClickHandler = { - (e: Event): void; -}; -``` -### ⚠️ Problem -Using the object literal syntax for single functions is unnecessarily complex and harder to read. -### ✅ Best Practice -```typescript -type ClickHandler = (e: Event) => void; -``` -### 🚀 Solution -Use the arrow function syntax for type aliases unless you need to define properties on the function itself (callable objects). - -## 17. Catching `any` in try-catch -**Context:** Handling exceptions. -### ❌ Bad Practice -```typescript -try { - doWork(); -} catch (e) { - console.error(e.message); // e is any by default -} -``` -### ⚠️ Problem -In JavaScript, anything can be thrown (`throw "error"`). Accessing `.message` on a string or null will crash. -### ✅ Best Practice -```typescript -try { - doWork(); -} catch (e) { - if (e instanceof Error) { - console.error(e.message); - } -} -``` -### 🚀 Solution -Always check the type of the caught error. In modern TS, use `useUnknownInCatchVariables: true` to force `e` to be `unknown`. - -## 18. Literal types vs General types -**Context:** Narrowing strings/numbers to specific values. -### ❌ Bad Practice -```typescript -function setAlignment(dir: string) { ... } -``` -### ⚠️ Problem -Accepting any `string` allows invalid inputs like `"center-left"` which the code won't handle. -### ✅ Best Practice -```typescript -type Direction = 'left' | 'right' | 'center'; -function setAlignment(dir: Direction) { ... } -``` -### 🚀 Solution -Use Union Literal types to restrict inputs to a known set of valid values. +## 📚 Specialized Topics -## 19. Optional properties vs Union with `undefined` -**Context:** Defining fields that might not exist. -### ❌ Bad Practice -```typescript -interface Config { - port: number | undefined; -} -``` -### ⚠️ Problem -This requires the key `port` to be present, even if its value is `undefined`. -### ✅ Best Practice -```typescript -interface Config { - port?: number; -} -``` -### 🚀 Solution -Use `?` for properties that can be omitted entirely. - -## 20. Array index access safety -**Context:** Accessing elements by index. -### ❌ Bad Practice -```typescript -const first = users[0]; -console.log(first.id); // Potential crash if array is empty -``` -### ⚠️ Problem -TypeScript assumes `users[0]` always exists if the array type is `User[]`. -### ✅ Best Practice -```typescript -const first = users[0]; -if (first) { - console.log(first.id); -} -``` -### 🚀 Solution -Enable `noUncheckedIndexedAccess: true` in `tsconfig.json`. This forces index access to return `T | undefined`. - ---- +For further reading, please refer to the following specialized guides: -## III. Objects & Functions (21-30) - -## 21. Object literals vs `Record` -**Context:** Defining maps/dictionaries. -### ❌ Bad Practice -```typescript -const prices: { [key: string]: number } = { apple: 1 }; -``` -### ⚠️ Problem -The index signature syntax is more verbose and harder to read. -### ✅ Best Practice -```typescript -const prices: Record = { apple: 1 }; -``` -### 🚀 Solution -Use the `Record` utility type for key-value maps. - -## 22. Excess property checks and object spreading -**Context:** Passing objects to functions. -### ❌ Bad Practice -```typescript -const extra = { id: 1, name: 'A', extra: true }; -saveUser({ id: 1, name: 'A', extra: true }); // Error: excess property -saveUser(extra); // No error, but 'extra' is leaked into db -``` -### ⚠️ Problem -Excess property checks only happen on object literals. Spreading or passing variables can bypass this, leading to data pollution. -### ✅ Best Practice -```typescript -const { extra, ...validUser } = data; -saveUser(validUser); -``` -### 🚀 Solution -Be explicit about what data you pass. Use destructuring to strip unknown properties before passing objects to storage or APIs. - -## 23. `Readonly` for Immutability -**Context:** Preventing accidental state mutation. -### ❌ Bad Practice -```typescript -function process(config: Config) { - config.port = 80; // Side effect! -} -``` -### ⚠️ Problem -Mutable inputs lead to unpredictable state changes and bugs that are hard to trace in large applications. -### ✅ Best Practice -```typescript -function process(config: Readonly) { - // config.port = 80; // Error -} -``` -### 🚀 Solution -Use `Readonly` for function parameters and `as const` for configuration objects to enforce immutability at compile time. - -## 24. `Awaited` for Promise Unwrapping -**Context:** Getting the resolved type of a Promise. -### ❌ Bad Practice -```typescript -type Result = typeof apiCall extends Promise ? U : never; -``` -### ⚠️ Problem -Manually unwrapping promises via conditional types is complex and doesn't handle nested promises. -### ✅ Best Practice -```typescript -type Result = Awaited>; -``` -### 🚀 Solution -Use the `Awaited` utility type (TS 4.5+) for clean promise unwrapping. - -## 25. `this` typing in functions -**Context:** Ensuring correct context in callback-heavy code. -### ❌ Bad Practice -```typescript -function handleClick(this: any, event: Event) { - this.classList.add('active'); -} -``` -### ⚠️ Problem -`this` defaults to `any`, making it easy to access properties that don't exist on the actual execution context. -### ✅ Best Practice -```typescript -function handleClick(this: HTMLElement, event: Event) { - this.classList.add('active'); -} -``` -### 🚀 Solution -Always type the first "fake" `this` parameter in functions that rely on a specific execution context. - -## 26. Constructor Shorthand -**Context:** Defining class properties. -### ❌ Bad Practice -```typescript -class User { - public name: string; - constructor(name: string) { - this.name = name; - } -} -``` -### ⚠️ Problem -Redundant repetition of property names in declaration, parameter, and assignment. -### ✅ Best Practice -```typescript -class User { - constructor(public readonly name: string) {} -} -``` -### 🚀 Solution -Use parameter properties in constructors to declare and initialize class members in one step. - -## 27. Abstract classes vs Interfaces -**Context:** Defining blueprints for classes. -### ❌ Bad Practice -```typescript -class BaseService { - getData() { throw new Error("Not implemented"); } -} -``` -### ⚠️ Problem -Normal classes don't force implementation, leading to runtime errors. Interfaces don't allow shared logic. -### ✅ Best Practice -```typescript -abstract class BaseService { - abstract getData(): Promise; - log(msg: string) { console.log(msg); } -} -``` -### 🚀 Solution -Use `abstract` classes when you need to provide shared logic while forcing sub-classes to implement specific methods. - -## 28. Private vs `#private` -**Context:** Encapsulating data in classes. -### ❌ Bad Practice -```typescript -class User { - private secret = 123; -} -console.log(user['secret']); // Works at runtime -``` -### ⚠️ Problem -TypeScript's `private` keyword is only for compile-time. At runtime, the property is fully accessible. -### ✅ Best Practice -```typescript -class User { - #secret = 123; -} -``` -### 🚀 Solution -Use ES2020 `#private` fields for true runtime encapsulation if you are building libraries or high-security components. - -## 29. Decorators (Legacy vs TC39) -**Context:** Meta-programming in TypeScript. -### ❌ Bad Practice -```typescript -// Using experimentalDecorators: true -@Logged -class MyClass {} -``` -### ⚠️ Problem -Legacy decorators are non-standard and might break in future versions of Node/Browsers. -### ✅ Best Practice -Use the new TC39 Decorators (TS 5.0+) which align with the official JavaScript proposal. -### 🚀 Solution -If starting a new project, avoid decorators unless using a framework that mandates them (like NestJS or Angular). - -## 30. Utility Types (`Omit`, `Pick`, `Partial`) -**Context:** Transforming existing types. -### ❌ Bad Practice -```typescript -interface UserUpdate { - name?: string; - age?: number; -} -``` -### ⚠️ Problem -Manual re-declaration of properties leads to synchronization issues when the base `User` interface changes. -### ✅ Best Practice -```typescript -type UserUpdate = Partial>; -``` -### 🚀 Solution -Always derive sub-types from the source of truth using built-in utility types. - ---- - -## IV. Professional & Niche (31-40) - -## 31. Using `satisfies` to preserve literal types -**Context:** Checking an object against a type without widening it. -### ❌ Bad Practice -```typescript -const config: Record = { - host: 'localhost' -}; -// config.host is type 'string', not 'localhost' -``` -### ⚠️ Problem -Direct type annotation widens properties to the most general type, losing specific literal information needed for inference. -### ✅ Best Practice -```typescript -const config = { - host: 'localhost' -} satisfies Record; -// config.host is type 'localhost' -``` -### 🚀 Solution -Use `satisfies` (TS 4.9+). It validates the structure but preserves the narrowest possible type for the value. - -## 32. `const` type parameters (TS 5.0) -**Context:** Improving inference for generic constants. -### ❌ Bad Practice -```typescript -function route(paths: T) { ... } -route(['/home', '/about']); // T is string[] -``` -### ⚠️ Problem -Generic inference often widens string arrays to `string[]` unless the caller adds `as const`. -### ✅ Best Practice -```typescript -function route(paths: T) { ... } -route(['/home', '/about']); // T is readonly ['/home', '/about'] -``` -### 🚀 Solution -Use `const` before a type parameter to force the compiler to treat the input as a constant, preserving literal types without requiring the caller to use `as const`. - -## 33. Branding/Tagging for Nominal Typing -**Context:** Preventing accidental mixing of identical primitive types (e.g., `UserId` and `OrderId`). -### ❌ Bad Practice -```typescript -type UserId = string; -type OrderId = string; - -const ship = (u: UserId, o: OrderId) => {}; -ship('order_123', 'user_456'); // No error, but logic is wrong! -``` -### ⚠️ Problem -TypeScript is structural. Two type aliases of `string` are identical and interchangeable. -### ✅ Best Practice -```typescript -type Brand = K & { __brand: T }; -type UserId = Brand; -type OrderId = Brand; - -// Usage requires a cast at creation, but provides safety after -const uid = 'user_1' as UserId; -``` -### 🚀 Solution -Use "Branding" (adding a phantom property) to simulate nominal typing for critical identifiers. - -## 34. Covariance/Contravariance in callbacks -**Context:** Ensuring safe function assignments. -### ❌ Bad Practice -```typescript -interface Logger { - log: (msg: string) => void; // Defined as a property -} -``` -### ⚠️ Problem -Function properties are checked **bivariantly**, which is less strict and can lead to runtime errors when assigning functions with more specialized arguments. -### ✅ Best Practice -```typescript -interface Logger { - log(msg: string): void; // Defined as a method -} -``` -### 🚀 Solution -Use method syntax in interfaces for stricter **contravariant** checking of parameters. - -## 35. Avoiding "God Objects" through Mapped Types -**Context:** Transforming object structures dynamically. -### ❌ Bad Practice -```typescript -interface API { - getUser: () => void; - getPost: () => void; -} -``` -### ⚠️ Problem -Manually adding properties to large interfaces is repetitive and error-prone. -### ✅ Best Practice -```typescript -type Resource = 'User' | 'Post'; -type API = { - [K in Resource as `get${K}`]: () => void; -}; -``` -### 🚀 Solution -Use Mapped Types and Key Remapping (`as`) to generate interface structures from a single source of truth (like a union of keys). - -## 36. Template Literal Types for string-based APIs -**Context:** Enforcing patterns in strings. -### ❌ Bad Practice -```typescript -function setPadding(value: string) { ... } // Accepts "10" (invalid) -``` -### ⚠️ Problem -Strings used for CSS or IDs often have strict patterns that `string` doesn't capture. -### ✅ Best Practice -```typescript -type CssValue = `${number}${'px' | 'em' | 'rem'}`; -function setPadding(value: CssValue) { ... } -``` -### 🚀 Solution -Use Template Literal types to enforce specific string patterns at compile time. - -## 37. Exhaustiveness checking with `never` -**Context:** Ensuring all cases in a union are handled. -### ❌ Bad Practice -```typescript -function handle(action: 'START' | 'STOP') { - switch (action) { - case 'START': return 1; - case 'STOP': return 0; - } -} -``` -### ⚠️ Problem -If you add `'PAUSE'` to the union, the compiler won't warn you that `handle` is now missing a case. -### ✅ Best Practice -```typescript -function handle(action: 'START' | 'STOP' | 'PAUSE') { - switch (action) { - case 'START': return 1; - case 'STOP': return 0; - default: { - const _exhaustive: never = action; // Error: 'PAUSE' is not assignable to never - return _exhaustive; - } - } -} -``` -### 🚀 Solution -Assign the `default` case to a variable of type `never`. This triggers a compile error if any member of the union is unhandled. - -## 38. Recursive Type Aliases -**Context:** Modeling nested structures like JSON or file trees. -### ❌ Bad Practice -```typescript -type Json = string | number | boolean | JsonObject | JsonArray; -interface JsonObject { [key: string]: Json; } -``` -### ⚠️ Problem -Older TS versions required interfaces for recursion. Modern TS allows direct recursion. -### ✅ Best Practice -```typescript -type JSONValue = - | string | number | boolean | null - | { [key: string]: JSONValue } - | JSONValue[]; -``` -### 🚀 Solution -Use recursive type aliases for cleaner definitions of deeply nested data structures. - -## 39. `infer` keyword in conditional types -**Context:** Extracting internal types from complex structures. -### ❌ Bad Practice -```typescript -// Hardcoded extraction -type GetArrayType = T extends string[] ? string : never; -``` -### ⚠️ Problem -Hardcoding extractions limits reusability and doesn't scale. -### ✅ Best Practice -```typescript -type GetArrayType = T extends (infer U)[] ? U : never; -``` -### 🚀 Solution -Use `infer` within conditional types to let TypeScript dynamically capture and name types from within generics. - -## 40. Tuple types for fixed-length data -**Context:** Representing arrays with specific structures (e.g., coordinates). -### ❌ Bad Practice -```typescript -const point: number[] = [10, 20]; -const [x, y, z] = point; // z is undefined, but TS doesn't know -``` -### ⚠️ Problem -`number[]` doesn't capture the length, leading to potential out-of-bounds errors during destructuring. -### ✅ Best Practice -```typescript -const point: [number, number] = [10, 20]; -// const [x, y, z] = point; // Error: Tuple has 2 elements -``` -### 🚀 Solution -Use Tuples for arrays where the number of elements and their positions are fixed and meaningful. +- [🛡️ Logic & Safety](./logic-safety.md) +- [📦 Objects & Functions](./objects-functions.md) +- [🧠 Professional & Niche Topics](./professional-niche.md)