diff --git a/frontend/angular/data-forms.md b/frontend/angular/data-forms.md index 5d52000..5d4a4d4 100644 --- a/frontend/angular/data-forms.md +++ b/frontend/angular/data-forms.md @@ -20,15 +20,20 @@ last_updated: 2026-03-22 > [!NOTE] > **Context:** Form Safety ### ❌ Bad Practice -`[(ngModel)]` without strict model typing. +```html + +``` ### ⚠️ Problem -Risk of assigning a string to a numeric field. +Using `[(ngModel)]` without strict model typing risks assigning a string to a numeric field or vice versa, causing runtime errors and confusing data flow. ### ✅ Best Practice -Use Reactive Forms with `FormControl` typing or new Signal-based Forms (when out of developer preview). - - +```typescript +userAge = model(0); +``` +```html + +``` ### 🚀 Solution -This approach provides a deterministic, type-safe implementation that is resilient and Agent-Readable, maintaining strict architectural boundaries. +Use Signal-based `model()` inputs combined with strict HTML input types. This provides a deterministic, type-safe implementation that maintains strict architectural boundaries. ## ⚡ 47. Untyped `FormGroup` > [!NOTE] > **Context:** Reactive Forms @@ -70,56 +75,87 @@ Use Flattening Operators (`switchMap`, `concatMap`, `mergeMap`). > [!NOTE] > **Context:** Network Efficiency ### ❌ Bad Practice -Ignoring request cancellation when navigating away from the page. +```typescript +fetchData() { + this.http.get('/api/data').subscribe(data => this.data.set(data)); +} +``` ### ⚠️ Problem -Requests continue hanging, consuming traffic. +Ignoring request cancellation when navigating away from the page or making subsequent requests leads to hanging connections, memory leaks, and potential race conditions if old requests resolve after new ones. ### ✅ Best Practice -HttpClient automatically supports cancellation upon unsubscription. With signals: ensure `rxResource` or the effect correctly cancels the request. - - +```typescript +fetchData() { + this.http.get('/api/data').pipe(takeUntilDestroyed()).subscribe(data => this.data.set(data)); +} +``` ### 🚀 Solution -This approach provides a deterministic, type-safe implementation that is resilient and Agent-Readable, maintaining strict architectural boundaries. +Always tie HTTP requests to the component lifecycle using `takeUntilDestroyed()`. This automatically aborts pending requests when the context is destroyed, optimizing network efficiency and ensuring deterministic state. ## ⚡ 50. Mutating Inputs directly > [!NOTE] > **Context:** Unidirectional Data Flow ### ❌ Bad Practice ```typescript -this.inputData.push(newItem); +data = input([]); +addItem(newItem: Item) { + this.data().push(newItem); +} ``` ### ⚠️ Problem -The parent component remains unaware of the change. Violates the One-Way Data Flow principle. +Directly mutating an array or object received via input bypasses the reactivity system and violates the One-Way Data Flow principle. The parent component remains unaware of the change. ### ✅ Best Practice -Emit event (`output`) upwards; the parent changes the data and passes the new object downwards. - +```typescript +data = input([]); +dataChange = output(); +addItem(newItem: Item) { + this.dataChange.emit([...this.data(), newItem]); +} +``` ### 🚀 Solution -This approach provides a deterministic, type-safe implementation that is resilient and Agent-Readable, maintaining strict architectural boundaries. +Emit an event using the `output()` API upwards; the parent handles the mutation immutably and passes the new reference downwards. This maintains unidirectional data flow and ensures correct change detection. ## ⚡ 51. `ngModel` inside Reactive Form > [!NOTE] > **Context:** Form Mixing ### ❌ Bad Practice -Using `formControlName` and `[(ngModel)]` on the same input. +```html +
+ +
+``` ### ⚠️ Problem -Deprecated behavior. Causes form and model synchronization conflicts. +Mixing `formControlName` and `[(ngModel)]` is deprecated behavior. It creates two sources of truth, causing form and model synchronization conflicts and unpredictable value updates. ### ✅ Best Practice -Use only one approach: either Reactive or Template-driven. - - +```html +
+ +
+``` +```typescript +// Subscribe to value changes in component if needed +nameValue = toSignal(this.form.get('name').valueChanges); +``` ### 🚀 Solution -This approach provides a deterministic, type-safe implementation that is resilient and Agent-Readable, maintaining strict architectural boundaries. +Use only one approach strictly: Reactive Forms with `formControlName`. For reactivity, derive a signal from `valueChanges` using `toSignal()` instead of relying on two-way binding. ## ⚡ 52. Complex Validators in Template > [!NOTE] > **Context:** Form Logic ### ❌ Bad Practice -Validation via HTML attributes for complex logic. +```html + +``` ### ⚠️ Problem -Hard to test, no reusability. +Placing complex regex validations directly in HTML attributes creates code that is impossible to unit test independently, provides poor error messages, and lacks reusability. ### ✅ Best Practice -Custom Validator Functions or Async Validators in the component class. - +```typescript +const passwordValidator: ValidatorFn = (control: AbstractControl) => { + const valid = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/.test(control.value); + return valid ? null : { invalidPassword: true }; +}; +password = new FormControl('', [Validators.required, passwordValidator]); +``` ### 🚀 Solution -This approach provides a deterministic, type-safe implementation that is resilient and Agent-Readable, maintaining strict architectural boundaries. +Abstract complex logic into Custom Validator Functions within the TypeScript class. This ensures high testability, strong typing, and reusability across multiple forms. ## ⚡ 53. Forgetting `updateOn: 'blur'` > [!NOTE] > **Context:** Performance @@ -137,26 +173,43 @@ Trigger validation/update only when the user has finished typing. > [!NOTE] > **Context:** UX ### ❌ Bad Practice -`.subscribe(data => ...)` without an error callback. +```typescript +this.http.get('/api/user').subscribe(data => { + this.user.set(data); +}); +``` ### ⚠️ Problem -On a 500 error, the application "hangs" in a loading state. +Failing to handle errors leads to silent failures or unhandled exceptions in the console. On a 500 error, the application may "hang" in an infinite loading state, destroying the UX. ### ✅ Best Practice -Global Error Handler or `catchError` in the pipe returning a safe value. - - +```typescript +this.http.get('/api/user').pipe( + catchError(err => { + this.toastService.error('Failed to load user'); + return of(null); + }) +).subscribe(data => { + if (data) this.user.set(data); +}); +``` ### 🚀 Solution -This approach provides a deterministic, type-safe implementation that is resilient and Agent-Readable, maintaining strict architectural boundaries. +Always implement a `catchError` block in the RxJS pipe to handle API failures gracefully. Return a safe fallback value and notify the user to ensure deterministic application flow. ## ⚡ 55. Hardcoded API URLs > [!NOTE] > **Context:** Maintainability ### ❌ Bad Practice -`http.get('https://api.com/users')` +```typescript +this.http.get('https://api.production.com/users'); +``` ### ⚠️ Problem -Inability to switch environments (dev/prod). +Hardcoding API URLs directly into service methods completely couples the code to a specific environment, making it impossible to seamlessly deploy to staging or local dev environments without manual changes. ### ✅ Best Practice -Using InjectionToken `API_URL` and environment configuration. - +```typescript +export const API_URL = new InjectionToken('API_URL'); +// In service: +private apiUrl = inject(API_URL); +this.http.get(`${this.apiUrl}/users`); +``` ### 🚀 Solution -This approach provides a deterministic, type-safe implementation that is resilient and Agent-Readable, maintaining strict architectural boundaries. +Utilize an `InjectionToken` combined with environment configurations to provide the API URL. This ensures configuration is decoupled from business logic and allows deterministic dependency injection. --- diff --git a/frontend/angular/expert-niche.md b/frontend/angular/expert-niche.md index a481931..e090dad 100644 --- a/frontend/angular/expert-niche.md +++ b/frontend/angular/expert-niche.md @@ -55,28 +55,57 @@ Always initialize signals with the full object shape (even with null) to preserv > [!NOTE] > **Context:** Reactivity Theory ### ❌ Bad Practice -Relying on `effect` to fire synchronously. +```typescript +count = signal(0); +doubleCount = signal(0); + +constructor() { + effect(() => { + this.doubleCount.set(this.count() * 2); + }); +} +``` ### ⚠️ Problem -Signals guarantee "Glitch Freedom" (absence of intermediate inconsistent states), but effects are asynchronous (microtask timing). +Using `effect` to derive or synchronize local state is an anti-pattern. Effects are asynchronous (microtask timing), which means the state can momentarily be inconsistent ("glitch") before the effect runs, leading to UI flicker or bugs. ### ✅ Best Practice -Do not use effects to synchronize local state. Use `computed`. - - +```typescript +count = signal(0); +doubleCount = computed(() => this.count() * 2); +``` ### 🚀 Solution -This approach provides a deterministic, type-safe implementation that is resilient and Agent-Readable, maintaining strict architectural boundaries. +Use `computed` for derived state. Computed signals evaluate lazily and synchronously when read, ensuring true Glitch Freedom and eliminating unnecessary change detection cycles. ## ⚡ 59. Memory leaks in `root` Effects > [!NOTE] > **Context:** Application Lifecycle ### ❌ Bad Practice -Creating an effect in a service without `manualCleanup`. +```typescript +@Injectable({ providedIn: 'root' }) +export class GlobalService { + constructor() { + effect(() => { + // Listens to global events indefinitely + console.log('State changed', this.state()); + }); + } +} +``` ### ⚠️ Problem -Effects in `root` services live forever. If they subscribe to something global, it can leak. +Effects created in `root` services live for the entire lifecycle of the application. If the service relies on dynamic instantiation or lazy loading and is later destroyed, the effect will continue to execute, causing memory leaks and unexpected behavior. ### ✅ Best Practice -Usually fine, but if the service is destroyed (rare lazy loading case), the effect must be cleaned up with `effectRef.destroy()`. - +```typescript +@Injectable({ providedIn: 'root' }) +export class GlobalService implements OnDestroy { + private effectRef = effect(() => { + console.log('State changed', this.state()); + }, { manualCleanup: true }); + ngOnDestroy() { + this.effectRef.destroy(); + } +} +``` ### 🚀 Solution -This approach provides a deterministic, type-safe implementation that is resilient and Agent-Readable, maintaining strict architectural boundaries. +When creating effects in long-lived or dynamic services, explicitly configure them with `{ manualCleanup: true }` and destroy the reference during the service's `ngOnDestroy` lifecycle hook. This ensures deterministic resource management. ## 📖 60. `runInInjectionContext` > [!NOTE] > **Context:** Advanced DI diff --git a/frontend/javascript/modern-syntax.md b/frontend/javascript/modern-syntax.md index 53aa3d7..f29e3a5 100644 --- a/frontend/javascript/modern-syntax.md +++ b/frontend/javascript/modern-syntax.md @@ -151,24 +151,31 @@ Treat objects as immutable. Use the spread operator to create copies with update > **Context:** Simplifying conditional branching. ### ❌ Bad Practice ```javascript -switch (action) { - case 'CREATE': return doCreate(); - case 'UPDATE': return doUpdate(); - default: return doNothing(); +function executeAction(action) { + 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. +`switch` statements are verbose, require `break` to prevent fallthrough bugs if not returning immediately, and have a non-standard block scope. They also violate the Open/Closed Principle making it harder to extend dynamically. ### ✅ Best Practice ```javascript -const actions = { - CREATE: doCreate, - UPDATE: doUpdate -}; -return (actions[action] || doNothing)(); +function executeAction(action) { + 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. +Use an Object Literal (or Map) as a lookup table. It provides a deterministic, type-safe execution path that is cleaner, faster, and allows for dynamic extension without modifying the core logic. ## ⚡ 19. Not using Optional Chaining `?.` > [!NOTE] > **Context:** Safe property access in nested objects. diff --git a/frontend/qwik/performance.md b/frontend/qwik/performance.md index 0edda2a..e81b4dc 100644 --- a/frontend/qwik/performance.md +++ b/frontend/qwik/performance.md @@ -18,7 +18,7 @@ last_updated: 2026-03-22 ## ⚡ II. Advanced Performance -## ⚡ 1. Synchronous Closures +## 🚨 1. Synchronous Closures > [!NOTE] > **Context:** Component Event Handlers ### ❌ Bad Practice diff --git a/frontend/qwik/readme.md b/frontend/qwik/readme.md index cde5b3f..413fd04 100644 --- a/frontend/qwik/readme.md +++ b/frontend/qwik/readme.md @@ -26,7 +26,7 @@ last_updated: 2026-03-22 - 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 +## 🚨 1. Passing Closures as Props > [!NOTE] > **Context:** Component Props ### ❌ Bad Practice diff --git a/frontend/qwik/state-management.md b/frontend/qwik/state-management.md index 79b00f9..7de0fc7 100644 --- a/frontend/qwik/state-management.md +++ b/frontend/qwik/state-management.md @@ -18,7 +18,7 @@ last_updated: 2026-03-22 ## ⚡ II. State Management -## ⚡ 1. Storing Unserializable Data +## 🚨 1. Storing Unserializable Data > [!NOTE] > **Context:** Reactive Stores and State Object ### ❌ Bad Practice diff --git a/frontend/react/performance.md b/frontend/react/performance.md index d80ac86..81ddfd8 100644 --- a/frontend/react/performance.md +++ b/frontend/react/performance.md @@ -17,7 +17,7 @@ last_updated: 2026-03-22 - **Tech Stack Version:** React 19+ ## 📚 Topics -### 🔹 1. Manual Memoization vs React Compiler +### 🚨 1. Manual Memoization vs React Compiler > [!NOTE] > **Context:** Avoiding unnecessary re-renders. ### ❌ Bad Practice @@ -51,11 +51,9 @@ function UserList({ users }) { } ``` ### 🚀 Solution -Rely on the **React Compiler** (introduced in React 19+). The compiler automatically memoizes values and functions, meaning manual hooks are largely obsolete and code becomes purely declarative. -- **Performance Note:** The React Compiler analyzes your component structure and injects optimal memoization (similar to SolidJS's granular updates), eliminating the overhead of manual dependency tracking. -- **Security Note:** While the React Compiler does not directly impact security, it ensures components render exactly when their inputs change, reducing side effects that might otherwise expose temporary or stale data to users. +Rely on the **React Compiler** (introduced in React 19+). The compiler automatically memoizes values and functions, meaning manual hooks are largely obsolete and code becomes purely declarative. This optimizes components structurally and removes manual dependency tracking overhead. -### 🔹 2. Resolving Promises During Render +### 🚨 2. Resolving Promises During Render > [!NOTE] > **Context:** Conditionally handling promises without `useEffect` or `useState`. ### ❌ Bad Practice @@ -90,8 +88,6 @@ function Profile({ profilePromise }) { // ``` ### 🚀 Solution -Use the `use()` API inside components combined with ``. -- **Performance Note:** `use()` suspends the component rendering if the promise is not resolved. This seamlessly integrates with ``, providing a highly optimized rendering fallback behavior. -- **Security Note:** `use()` can also resolve context, mitigating prop-drilling vulnerabilities and ensuring components securely consume data directly from contexts they are explicitly authorized for. Always sanitize any text coming from external APIs before rendering. +Use the `use()` API inside components combined with ``. `use()` suspends the component rendering if the promise is not resolved. This seamlessly integrates with ``, providing a highly optimized rendering fallback behavior and ensuring safe resolution of asynchronous data. [⬆️ Back to Top](#) diff --git a/frontend/react/readme.md b/frontend/react/readme.md index 34fa17b..dc8eb5f 100644 --- a/frontend/react/readme.md +++ b/frontend/react/readme.md @@ -22,7 +22,7 @@ last_updated: 2026-03-22 - Strongly prefer **Feature Sliced Design (FSD)** for applications scaling across multiple teams. ## 🚀 I. Basics & Popular -### 🔹 1. Direct DOM Manipulation +### 🚨 1. Direct DOM Manipulation > [!NOTE] > **Context:** Updating elements in a React component. ### ❌ Bad Practice @@ -35,7 +35,7 @@ function Component() { } ``` ### ⚠️ Problem -Direct DOM manipulation bypasses React's virtual DOM, causing inconsistencies between the actual DOM and React's internal state. +Direct DOM manipulation bypasses React's virtual DOM, causing inconsistencies between the actual DOM and React's internal state, leading to forced synchronous layouts and potential XSS vulnerabilities. ### ✅ Best Practice ```tsx function Component() { @@ -51,21 +51,34 @@ function Component() { } ``` ### 🚀 Solution -Always use state and props to drive the UI. React uses a virtual DOM to efficiently update the real DOM based on state changes. -- **Performance Note:** React's virtual DOM diffing algorithm is highly optimized. Bypassing it can lead to forced synchronous layouts and jank. -- **Security Note:** Direct DOM manipulation can open up Cross-Site Scripting (XSS) vulnerabilities if user input is not properly sanitized before being inserted into the DOM. +Always use state and props to drive the UI. React uses a virtual DOM to efficiently update the real DOM based on state changes, ensuring deterministic rendering loops. -### 🔹 2. Large Component Files +### 🚨 2. Large Component Files > [!NOTE] > **Context:** Managing component complexity. ### ❌ Bad Practice -A single 2000-line file containing the entire page's logic and UI. +```tsx +function MassiveDashboard() { + // 500 lines of state and hooks + // 1000 lines of JSX layout + return
...
; +} +``` ### ⚠️ Problem -Massive components are difficult to read, test, and maintain. They often violate the Single Responsibility Principle. +Massive components are difficult to read, test, and maintain. They violate the Single Responsibility Principle and trigger overly broad re-renders when local state changes. ### ✅ Best Practice -Break down the UI into smaller, reusable components, each with a single responsibility. +```tsx +function Dashboard() { + return ( + + + + + ); +} +``` ### 🚀 Solution -Extract logic into custom hooks and presentational elements into separate files. +Break down the UI into smaller, reusable components. Extract logic into custom hooks and modularize presentational elements into separate, encapsulated files. ## 📚 Specialized Topics For further reading, please refer to the following specialized guides: diff --git a/frontend/react/state-management.md b/frontend/react/state-management.md index 2a873d5..67881db 100644 --- a/frontend/react/state-management.md +++ b/frontend/react/state-management.md @@ -17,7 +17,7 @@ last_updated: 2026-03-22 - **Tech Stack Version:** React 19+ ## 📚 Topics -### 🔹 1. Handling Async Actions (Forms) +### 🚨 1. Handling Async Actions (Forms) > [!NOTE] > **Context:** Managing state updates triggered by form submissions or asynchronous operations. ### ❌ Bad Practice @@ -45,7 +45,7 @@ function Form() { } ``` ### ⚠️ Problem -Manually managing `isPending` and error states is repetitive and prone to race conditions, especially when multiple requests are fired. +Manually managing `isPending` and error states is repetitive and prone to race conditions, especially when multiple requests are fired. It creates unnecessary state overhead. ### ✅ Best Practice ```tsx import { useActionState } from 'react'; @@ -63,22 +63,33 @@ function Form() { } ``` ### 🚀 Solution -Use the `useActionState` Hook (React 19+) for seamless action state management. -- **Performance Note:** `useActionState` effectively handles race conditions by ensuring only the latest action state is applied to the UI, optimizing rendering cycles. -- **Security Note:** Form actions seamlessly interact with Server Actions. Ensure that `saveAction` strictly validates input server-side to prevent malicious payloads, and use CSRF tokens if required by your framework. +Use the `useActionState` Hook (React 19+) for seamless action state management. This hook natively handles loading and error states, resolves race conditions by ensuring only the latest action state is applied to the UI, and optimizes rendering cycles deterministically. -### 🔹 2. Using Global State Naively +### 🚨 2. Using Global State Naively > [!NOTE] > **Context:** Storing local component UI state in a global store (e.g., Redux, Zustand). ### ❌ Bad Practice -Putting a dropdown's `isOpen` state into the global Redux store. +```tsx +import { useStore } from './store'; + +function Dropdown() { + const isOpen = useStore(state => state.isDropdownOpen); + const toggle = useStore(state => state.toggleDropdown); + return
{isOpen ? 'Open' : 'Closed'}
; +} +``` ### ⚠️ Problem -Unnecessary global re-renders and bloated global state size. +Putting local, ephemeral UI state (like a dropdown's `isOpen` flag) into a global store causes unnecessary global re-renders, inflates store complexity, and couples isolated UI logic to the global application state. ### ✅ Best Practice -Use `useState` or `useReducer` for UI state that belongs locally to a component tree. +```tsx +import { useState } from 'react'; + +function Dropdown() { + const [isOpen, setIsOpen] = useState(false); + return
setIsOpen(!isOpen)}>{isOpen ? 'Open' : 'Closed'}
; +} +``` ### 🚀 Solution -Only elevate state to a global store when it is shared across multiple disjoint component branches. -- **Performance Note:** Global state updates trigger broad change detection and React reconciliation. Minimizing global state keeps updates localized and fast. -- **Security Note:** Do not store sensitive access tokens (e.g., JWT) in unencrypted global state (like localStorage/Redux state) that may persist across sessions or expose them to XSS attacks. Prefer HttpOnly cookies. +Use `useState` or `useReducer` for UI state that belongs locally to a component tree. Only elevate state to a global store when it is shared across multiple disjoint component branches, maintaining strict state colocation for optimal rendering performance. [⬆️ Back to Top](#) diff --git a/frontend/solidjs/readme.md b/frontend/solidjs/readme.md index 1584b51..1db2a2a 100644 --- a/frontend/solidjs/readme.md +++ b/frontend/solidjs/readme.md @@ -26,7 +26,7 @@ last_updated: 2026-03-22 - 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 +## 🚨 1. Using JSX Map for Lists > [!NOTE] > **Context:** Rendering Lists ### ❌ Bad Practice diff --git a/frontend/typescript/logic-safety.md b/frontend/typescript/logic-safety.md index 24e466a..e564903 100644 --- a/frontend/typescript/logic-safety.md +++ b/frontend/typescript/logic-safety.md @@ -16,7 +16,7 @@ last_updated: 2026-03-22 - **Target Tooling:** Cursor, Windsurf, Antigravity. - **Tech Stack Version:** TypeScript 5.5+ ## ⚡ II. Logic & Safety (11-20) -## ⚡ 11. Type Assertions (`as`) vs Narrowing +## 🚨 11. Type Assertions (`as`) vs Narrowing > [!NOTE] > **Context:** Telling the compiler what a type is. ### ❌ Bad Practice @@ -25,16 +25,19 @@ 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. +`as` forces the compiler to trust you. If the runtime data doesn't match the interface, the app will crash or produce undefined behavior at runtime. ### ✅ Best Practice ```typescript -const user = UserSchema.parse(response.data); // Using Zod for runtime validation +// Using Zod for runtime validation +const user = UserSchema.parse(response.data); // OR -if (isValidUser(response.data)) { ... } +if (isValidUser(response.data)) { + console.log(response.data.id); +} ``` ### 🚀 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 (`!`) +Avoid type assertions. Use runtime validation (Zod, Valibot) or explicit Type Guards to deterministically ensure the data matches the expected type before processing. +## 🚨 12. Non-null Assertion Operator (`!`) > [!NOTE] > **Context:** Dealing with potentially `null` or `undefined` values. ### ❌ Bad Practice @@ -42,14 +45,14 @@ Avoid type assertions. Use runtime validation (Zod, Valibot) or Type Guards to e 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`. +The `!` operator suppresses the compiler warning but doesn't handle the runtime reality. If `user` is null, this throws a `TypeError` and crashes the application. ### ✅ 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 +Use Optional Chaining (`?.`) and Nullish Coalescing (`??`) to handle missing values gracefully, preventing runtime crashes and ensuring a deterministic UI state. +## 🚨 13. Lack of Discriminated Unions > [!NOTE] > **Context:** Modeling complex states like API responses. ### ❌ Bad Practice @@ -61,17 +64,17 @@ interface State { } ``` ### ⚠️ Problem -This allows "impossible states" (e.g., `isLoading: true` AND `data: '...'`). It requires awkward optional checking. +This allows "impossible states" (e.g., `isLoading: true` AND `data: '...'`). It requires awkward optional checking and fails to enforce a deterministic state machine. ### ✅ Best Practice ```typescript type State = | { type: 'LOADING' } - | { type: 'SUCCESS', data: string } - | { type: 'ERROR', error: string }; + | { 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 (`!!`) +Use Discriminated Unions (with a shared literal property like `type` or `kind`). This ensures mutually exclusive states, simplifying logic and providing full compiler support for exhaustive type narrowing. +## 🚨 14. Boolean casting (`!!`) > [!NOTE] > **Context:** Converting values to booleans. ### ❌ Bad Practice @@ -79,7 +82,7 @@ Use Discriminated Unions (with a shared literal property like `type` or `kind`). 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. +`!!` is cryptic and less readable. It also doesn't provide strict type safety if the underlying value could be unexpectedly complex or result in unintended truthy evaluation. ### ✅ Best Practice ```typescript const hasAccess = Boolean(user.token); @@ -87,23 +90,27 @@ const hasAccess = Boolean(user.token); const hasAccess = user.token !== undefined; ``` ### 🚀 Solution -Use the `Boolean()` constructor or explicit comparisons for clarity. -## ⚡ 15. Using `Object` for non-primitive types +Use the `Boolean()` constructor or explicit strict comparisons (`!== undefined`). This enforces explicit casting and declarative, agent-readable intent. +## 🚨 15. Using `Object` for non-primitive types > [!NOTE] > **Context:** Restricting types to objects. ### ❌ Bad Practice ```typescript -function cache(obj: Object) { ... } +function cache(obj: Object) { + // obj can be a string! +} ``` ### ⚠️ Problem -The `Object` type (capital O) includes primitives like `string` or `number` because they have methods. `object` (lowercase) is also vague. +The `Object` type (capital O) incorrectly matches primitives like `string` or `number` because they have boxed methods. `object` (lowercase) is similarly vague and offers poor intellisense. ### ✅ Best Practice ```typescript -function cache(obj: Record) { ... } +function cache(obj: Record) { + // Safe object access +} ``` ### 🚀 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 +Use `Record` for generic key-value maps, or `Record` for explicitly empty objects. This enforces structured object shapes strictly without matching arbitrary primitives. +## 🚨 16. Function types vs Object types for functions > [!NOTE] > **Context:** Defining function signatures. ### ❌ Bad Practice @@ -113,14 +120,14 @@ type ClickHandler = { }; ``` ### ⚠️ Problem -Using the object literal syntax for single functions is unnecessarily complex and harder to read. +Using the object literal syntax for single functions is unnecessarily complex, less intuitive, and introduces noise when reading the codebase. ### ✅ 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 +Use the arrow function signature for type aliases. Reserve the object-literal callable signature exclusively for functions that contain static properties attached to the function reference itself. +## 🚨 17. Catching `any` in try-catch > [!NOTE] > **Context:** Handling exceptions. ### ❌ Bad Practice @@ -132,36 +139,42 @@ try { } ``` ### ⚠️ Problem -In JavaScript, anything can be thrown (`throw "error"`). Accessing `.message` on a string or null will crash. +In JavaScript, anything can be thrown (`throw "error"`). Accessing `.message` blindly will throw a new exception if the caught element is a primitive string or null. ### ✅ Best Practice ```typescript try { doWork(); -} catch (e) { +} catch (e: unknown) { if (e instanceof Error) { console.error(e.message); + } else { + console.error(String(e)); } } ``` ### 🚀 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 +Ensure `useUnknownInCatchVariables: true` is configured in `tsconfig.json`. Explicitly annotate catch variables as `unknown` and implement type guards (like `instanceof Error`) to safely process the error payload. +## 🚨 18. Literal types vs General types > [!NOTE] > **Context:** Narrowing strings/numbers to specific values. ### ❌ Bad Practice ```typescript -function setAlignment(dir: string) { ... } +function setAlignment(dir: string) { + // Any random string can be passed +} ``` ### ⚠️ Problem -Accepting any `string` allows invalid inputs like `"center-left"` which the code won't handle. +Accepting any generic `string` allows invalid inputs like `"center-left"` which the function won't properly handle, shifting responsibility to runtime validation. ### ✅ Best Practice ```typescript type Direction = 'left' | 'right' | 'center'; -function setAlignment(dir: Direction) { ... } +function setAlignment(dir: Direction) { + // Safely execute alignment logic +} ``` ### 🚀 Solution -Use Union Literal types to restrict inputs to a known set of valid values. -## ⚡ 19. Optional properties vs Union with `undefined` +Leverage Union Literal types to constrain inputs to a closed set of known valid values, enforcing correctness entirely at compile time. +## 🚨 19. Optional properties vs Union with `undefined` > [!NOTE] > **Context:** Defining fields that might not exist. ### ❌ Bad Practice @@ -169,18 +182,24 @@ Use Union Literal types to restrict inputs to a known set of valid values. interface Config { port: number | undefined; } + +// Caller must do: +const cfg: Config = { port: undefined }; ``` ### ⚠️ Problem -This requires the key `port` to be present, even if its value is `undefined`. +Declaring a union with `undefined` still requires the key `port` to be declared explicitly by the caller, creating needless boilerplate. ### ✅ Best Practice ```typescript interface Config { port?: number; } + +// Caller can just do: +const cfg: Config = {}; ``` ### 🚀 Solution -Use `?` for properties that can be omitted entirely. -## ⚡ 20. Array index access safety +Use the optional modifier (`?`) for object properties that can be legally omitted. +## 🚨 20. Array index access safety > [!NOTE] > **Context:** Accessing elements by index. ### ❌ Bad Practice @@ -189,14 +208,15 @@ 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[]`. +By default, TypeScript assumes any indexed access like `users[0]` perfectly resolves to the array element type, which leads to `Cannot read property 'id' of undefined` if the array is actually empty. ### ✅ Best Practice ```typescript +// Assumes noUncheckedIndexedAccess is true 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`. +Enforce `noUncheckedIndexedAccess: true` in `tsconfig.json`. This strict compiler flag forces all array index access to resolve to `T | undefined`, mandating explicit nil-checks before usage. --- diff --git a/frontend/typescript/objects-functions.md b/frontend/typescript/objects-functions.md index 9882973..f4af418 100644 --- a/frontend/typescript/objects-functions.md +++ b/frontend/typescript/objects-functions.md @@ -16,7 +16,7 @@ last_updated: 2026-03-22 - **Target Tooling:** Cursor, Windsurf, Antigravity. - **Tech Stack Version:** TypeScript 5.5+ ## ⚡ III. Objects & Functions (21-30) -## ⚡ 21. Object literals vs `Record` +## 🚨 21. Object literals vs `Record` > [!NOTE] > **Context:** Defining maps/dictionaries. ### ❌ Bad Practice @@ -24,32 +24,33 @@ last_updated: 2026-03-22 const prices: { [key: string]: number } = { apple: 1 }; ``` ### ⚠️ Problem -The index signature syntax is more verbose and harder to read. +The index signature syntax is more verbose, harder to read, and less semantically clear when representing dictionaries or lookups compared to utility types. ### ✅ 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 +Use the `Record` utility type for key-value maps. It provides a deterministic, clean, and declarative syntax that AI agents and engineers can parse instantly. +## 🚨 22. Excess property checks and object spreading > [!NOTE] > **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 +const inputData = { id: 1, name: 'A', maliciousField: true }; +saveUser({ id: 1, name: 'A', maliciousField: true }); // Error: excess property +saveUser(inputData); // No error! 'maliciousField' is leaked into db ``` ### ⚠️ Problem -Excess property checks only happen on object literals. Spreading or passing variables can bypass this, leading to data pollution. +Excess property checks only apply to inline object literals. Passing pre-defined variables bypasses this compiler check, leading to data pollution or security vulnerabilities (like Mass Assignment). ### ✅ Best Practice ```typescript -const { extra, ...validUser } = data; +// Assume User type has only `id` and `name` +const { maliciousField, ...validUser } = inputData; 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 +Be strictly explicit about data payload boundaries. Use destructuring to strip unknown or unsafe properties before passing objects to persistence layers or external APIs, guaranteeing deterministic data shapes. +## 🚨 23. `Readonly` for Immutability > [!NOTE] > **Context:** Preventing accidental state mutation. ### ❌ Bad Practice @@ -59,16 +60,16 @@ function process(config: Config) { } ``` ### ⚠️ Problem -Mutable inputs lead to unpredictable state changes and bugs that are hard to trace in large applications. +Mutable inputs lead to unpredictable state changes, side effects, and bugs that are notoriously difficult to trace across large application boundaries. ### ✅ Best Practice ```typescript function process(config: Readonly) { - // config.port = 80; // Error + // config.port = 80; // TS Error: Cannot assign to 'port' because it is a read-only property. } ``` ### 🚀 Solution -Use `Readonly` for function parameters and `as const` for configuration objects to enforce immutability at compile time. -## ⚡ 24. `Awaited` for Promise Unwrapping +Use `Readonly` for function parameters and `as const` for configuration objects. This enforces strict immutability at compile time, guaranteeing predictable and pure function execution. +## 🚨 24. `Awaited` for Promise Unwrapping > [!NOTE] > **Context:** Getting the resolved type of a Promise. ### ❌ Bad Practice @@ -76,33 +77,33 @@ Use `Readonly` for function parameters and `as const` for configuration objec type Result = typeof apiCall extends Promise ? U : never; ``` ### ⚠️ Problem -Manually unwrapping promises via conditional types is complex and doesn't handle nested promises. +Manually unwrapping promises via custom conditional types is unnecessarily complex, less readable, and fails to properly recursively unwrap nested promises natively. ### ✅ Best Practice ```typescript type Result = Awaited>; ``` ### 🚀 Solution -Use the `Awaited` utility type (TS 4.5+) for clean promise unwrapping. -## ⚡ 25. `this` typing in functions +Always use the built-in `Awaited` utility type (TS 4.5+) for deterministic and clean promise resolution. +## 🚨 25. `this` typing in functions > [!NOTE] > **Context:** Ensuring correct context in callback-heavy code. ### ❌ Bad Practice ```typescript -function handleClick(this: unknown, event: Event) { - this.classList.add('active'); +function handleClick(event: Event) { + this.classList.add('active'); // 'this' is implicitly 'any' } ``` ### ⚠️ Problem -`this` defaults to `any`, making it easy to access properties that don't exist on the actual execution context. +`this` defaults to `any` in unbound functions, making it trivial to access properties that don't exist on the execution context and bypassing compiler safety nets. ### ✅ Best Practice ```typescript function handleClick(this: HTMLElement, event: Event) { - this.classList.add('active'); + this.classList.add('active'); // Safe, 'this' is HTMLElement } ``` ### 🚀 Solution -Always type the first "fake" `this` parameter in functions that rely on a specific execution context. -## ⚡ 26. Constructor Shorthand +Always type the first pseudo-parameter `this` explicitly in functions that rely on dynamic execution contexts. This provides a deterministic context for the compiler and Vibe Coding agents. +## 🚨 26. Constructor Shorthand > [!NOTE] > **Context:** Defining class properties. ### ❌ Bad Practice @@ -115,7 +116,7 @@ class User { } ``` ### ⚠️ Problem -Redundant repetition of property names in declaration, parameter, and assignment. +Redundant repetition of property names in the declaration, constructor parameter, and assignment block creates unnecessary boilerplate and increases cognitive load. ### ✅ Best Practice ```typescript class User { @@ -123,8 +124,8 @@ class User { } ``` ### 🚀 Solution -Use parameter properties in constructors to declare and initialize class members in one step. -## ⚡ 27. Abstract classes vs Interfaces +Leverage TypeScript parameter properties in constructors to declare and initialize class members in a single deterministic step, minimizing noise. +## 🚨 27. Abstract classes vs Interfaces > [!NOTE] > **Context:** Defining blueprints for classes. ### ❌ Bad Practice @@ -134,7 +135,7 @@ class BaseService { } ``` ### ⚠️ Problem -Normal classes don't force implementation, leading to runtime errors. Interfaces don't allow shared logic. +Using concrete classes as blueprints by throwing runtime errors fails to enforce implementation contracts at compile time. Interfaces alone cannot provide shared implementation logic. ### ✅ Best Practice ```typescript abstract class BaseService { @@ -143,8 +144,8 @@ abstract class BaseService { } ``` ### 🚀 Solution -Use `abstract` classes when you need to provide shared logic while forcing sub-classes to implement specific methods. -## ⚡ 28. Private vs `#private` +Utilize `abstract` classes when requiring shared implementation logic combined with strict contractual enforcement of specific methods by subclasses. +## 🚨 28. Private vs `#private` > [!NOTE] > **Context:** Encapsulating data in classes. ### ❌ Bad Practice @@ -152,49 +153,68 @@ Use `abstract` classes when you need to provide shared logic while forcing sub-c class User { private secret = 123; } -console.log(user['secret']); // Works at runtime +// Malicious user circumvents compiler: +console.log((user as any)['secret']); // Works at runtime! ``` ### ⚠️ Problem -TypeScript's `private` keyword is only for compile-time. At runtime, the property is fully accessible. +TypeScript's `private` keyword is a compile-time illusion. At runtime, the property remains fully exposed and accessible via bracket notation or generic type stripping. ### ✅ Best Practice ```typescript class User { #secret = 123; + getSecretSafely() { + return this.#secret; + } } ``` ### 🚀 Solution -Use ES2020 `#private` fields for true runtime encapsulation if you are building libraries or high-security components. -## ⚡ 29. Decorators (Legacy vs TC39) +Implement ES2020 `#private` fields for guaranteed runtime encapsulation, specifically when architecting SDKs, libraries, or processing highly secure data. +## 🚨 29. Decorators (Legacy vs TC39) > [!NOTE] > **Context:** Meta-programming in TypeScript. ### ❌ Bad Practice ```typescript -// Using experimentalDecorators: true +// Requires: "experimentalDecorators": true +function Logged(constructor: Function) { /* ... */ } + @Logged class MyClass {} ``` ### ⚠️ Problem -Legacy decorators are non-standard and might break in future versions of Node/Browsers. +Legacy decorators rely on the deprecated `experimentalDecorators` flag, misaligning with the standardized ECMAScript proposal and risking future deprecation breakage. ### ✅ Best Practice -Use the new TC39 Decorators (TS 5.0+) which align with the official JavaScript proposal. +```typescript +// TC39 standard (TS 5.0+) +function Logged any>( + value: Class, + context: ClassDecoratorContext +) { /* ... */ } + +@Logged +class MyClass {} +``` ### 🚀 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`) +Migrate to TC39 Standard Decorators supported in TypeScript 5.0+. Unless dictated by an explicit framework architecture (like NestJS or Angular), strictly use standard implementations. +## 🚨 30. Utility Types (`Omit`, `Pick`, `Partial`) > [!NOTE] > **Context:** Transforming existing types. ### ❌ Bad Practice ```typescript +interface User { name: string; age: number; role: string; } + interface UserUpdate { name?: string; age?: number; } ``` ### ⚠️ Problem -Manual re-declaration of properties leads to synchronization issues when the base `User` interface changes. +Manual re-declaration of properties leads to critical synchronization issues when the base `User` interface architecture evolves, creating fractured type contracts. ### ✅ Best Practice ```typescript +interface User { name: string; age: number; role: string; } + type UserUpdate = Partial>; ``` ### 🚀 Solution -Always derive sub-types from the source of truth using built-in utility types. +Always derive sub-types deterministically from the single source of truth using built-in utility types (`Pick`, `Omit`, `Partial`). --- diff --git a/frontend/typescript/professional-niche.md b/frontend/typescript/professional-niche.md index c17de56..e2525f9 100644 --- a/frontend/typescript/professional-niche.md +++ b/frontend/typescript/professional-niche.md @@ -16,7 +16,7 @@ last_updated: 2026-03-22 - **Target Tooling:** Cursor, Windsurf, Antigravity. - **Tech Stack Version:** TypeScript 5.5+ ## ⚡ IV. Professional & Niche (31-40) -## ⚡ 31. Using `satisfies` to preserve literal types +## 🚨 31. Using `satisfies` to preserve literal types > [!NOTE] > **Context:** Checking an object against a type without widening it. ### ❌ Bad Practice @@ -24,37 +24,37 @@ last_updated: 2026-03-22 const config: Record = { host: 'localhost' }; -// config.host is type 'string', not 'localhost' +// config.host is widened to type 'string' ``` ### ⚠️ Problem -Direct type annotation widens properties to the most general type, losing specific literal information needed for inference. +Direct type annotation widens properties to the most general type defined by the interface, losing specific literal information needed for precise inference downstream. ### ✅ Best Practice ```typescript const config = { host: 'localhost' } satisfies Record; -// config.host is type 'localhost' +// config.host is precisely 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) +Leverage the `satisfies` operator (TS 4.9+). It deterministically validates the object structure against a type while preserving the narrowest possible literal types for its properties. +## 🚨 32. `const` type parameters (TS 5.0) > [!NOTE] > **Context:** Improving inference for generic constants. ### ❌ Bad Practice ```typescript -function route(paths: T) { ... } -route(['/home', '/about']); // T is string[] +function route(paths: T) { /* ... */ } +route(['/home', '/about']); // T is widened to string[] ``` ### ⚠️ Problem -Generic inference often widens string arrays to `string[]` unless the caller adds `as const`. +Generic inference widens string arrays to `string[]` by default, forcing the caller to remember to append `as const` to retain literal type safety. ### ✅ Best Practice ```typescript -function route(paths: T) { ... } -route(['/home', '/about']); // T is readonly ['/home', '/about'] +function route(paths: T) { /* ... */ } +route(['/home', '/about']); // T is automatically inferred as 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 +Inject the `const` modifier before generic type parameters. This instructs the compiler to evaluate the input deterministically as a constant, preserving narrow literal types and shifting the burden of safety from the caller to the architecture. +## 🚨 33. Branding/Tagging for Nominal Typing > [!NOTE] > **Context:** Preventing accidental mixing of identical primitive types (e.g., `UserId` and `OrderId`). ### ❌ Bad Practice @@ -63,22 +63,22 @@ type UserId = string; type OrderId = string; const ship = (u: UserId, o: OrderId) => {}; -ship('order_123', 'user_456'); // No error, but logic is wrong! +ship('order_123', 'user_456'); // Compiles successfully, but business logic fails silently! ``` ### ⚠️ Problem -TypeScript is structural. Two type aliases of `string` are identical and interchangeable. +TypeScript utilizes structural typing. Two distinct type aliases resolving to `string` are evaluated identically and are interchangeable, risking silent logical data corruption. ### ✅ Best Practice ```typescript type Brand = K & { __brand: T }; type UserId = Brand; type OrderId = Brand; -// Usage requires a cast at creation, but provides safety after +// Instantiation requires explicit casting, but downstream usage is strictly protected 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 +Implement "Branding" (injecting a phantom, non-existent property via intersection) to simulate nominal typing for critical identifiers. This guarantees distinct domains for identical primitives. +## 🚨 34. Covariance/Contravariance in callbacks > [!NOTE] > **Context:** Ensuring safe function assignments. ### ❌ Bad Practice @@ -88,7 +88,7 @@ interface Logger { } ``` ### ⚠️ Problem -Function properties are checked **bivariantly**, which is less strict and can lead to runtime errors when assigning functions with more specialized arguments. +Function properties declared via arrow syntax are checked **bivariantly**. This leniency permits assigning functions with overly specialized arguments, creating unsafe runtime conditions. ### ✅ Best Practice ```typescript interface Logger { @@ -96,8 +96,8 @@ interface Logger { } ``` ### 🚀 Solution -Use method syntax in interfaces for stricter **contravariant** checking of parameters. -## ⚡ 35. Avoiding "God Objects" through Mapped Types +Always use method syntax in interfaces for callbacks and methods. This enforces strict **contravariant** checking of parameters, maintaining a deterministic architectural boundary. +## 🚨 35. Avoiding "God Objects" through Mapped Types > [!NOTE] > **Context:** Transforming object structures dynamically. ### ❌ Bad Practice @@ -105,36 +105,38 @@ Use method syntax in interfaces for stricter **contravariant** checking of param interface API { getUser: () => void; getPost: () => void; + // adding hundreds of manual methods... } ``` ### ⚠️ Problem -Manually adding properties to large interfaces is repetitive and error-prone. +Manually maintaining properties on massive generic interfaces is highly repetitive, scales poorly, and is prone to human error when synchronization is required. ### ✅ Best Practice ```typescript -type Resource = 'User' | 'Post'; +type Resource = 'User' | 'Post' | 'Comment'; 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 +Leverage Mapped Types combined with Key Remapping (`as`) to programmatically generate interface structures from a single source of truth, establishing an autonomous scaling pattern. +## 🚨 36. Template Literal Types for string-based APIs > [!NOTE] > **Context:** Enforcing patterns in strings. ### ❌ Bad Practice ```typescript -function setPadding(value: string) { ... } // Accepts "10" (invalid) +function setPadding(value: string) { /* ... */ } +setPadding("10"); // Accepts invalid CSS string without a unit! ``` ### ⚠️ Problem -Strings used for CSS or IDs often have strict patterns that `string` doesn't capture. +Accepting raw `string` types for structured domains (like CSS properties or UUIDs) fails to capture semantic patterns, pushing formatting errors into the runtime environment. ### ✅ Best Practice ```typescript type CssValue = `${number}${'px' | 'em' | 'rem'}`; -function setPadding(value: CssValue) { ... } +function setPadding(value: CssValue) { /* ... */ } ``` ### 🚀 Solution -Use Template Literal types to enforce specific string patterns at compile time. -## ⚡ 37. Exhaustiveness checking with `never` +Utilize Template Literal types to explicitly map and enforce semantic string patterns at compile time, eliminating an entire class of runtime parsing errors. +## 🚨 37. Exhaustiveness checking with `never` > [!NOTE] > **Context:** Ensuring all cases in a union are handled. ### ❌ Bad Practice @@ -147,7 +149,7 @@ function handle(action: 'START' | 'STOP') { } ``` ### ⚠️ Problem -If you add `'PAUSE'` to the union, the compiler won't warn you that `handle` is now missing a case. +If the union is expanded (e.g., adding `'PAUSE'`), the compiler will not natively warn that the `switch` statement is missing logic to handle the new case, causing unhandled runtime paths. ### ✅ Best Practice ```typescript function handle(action: 'START' | 'STOP' | 'PAUSE') { @@ -155,63 +157,69 @@ function handle(action: 'START' | 'STOP' | 'PAUSE') { case 'START': return 1; case 'STOP': return 0; default: { - const _exhaustive: never = action; // Error: 'PAUSE' is not assignable to never + // TS Error: Type 'string' is not assignable to type 'never'. + const _exhaustive: never = action; 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 +Assign the unhandled `default` case to a variable strictly typed as `never`. This enforces deterministic exhaustiveness checking, immediately triggering a compile error if a union member is omitted. +## 🚨 38. Recursive Type Aliases > [!NOTE] > **Context:** Modeling nested structures like JSON or file trees. ### ❌ Bad Practice ```typescript type Json = string | number | boolean | JsonObject | JsonArray; interface JsonObject { [key: string]: Json; } +// Needs multiple declarations just to support recursion ``` ### ⚠️ Problem -Older TS versions required interfaces for recursion. Modern TS allows direct recursion. +Older paradigms required bridging type aliases with interfaces to achieve recursion, cluttering the domain space with artificial helper types. ### ✅ Best Practice ```typescript type JSONValue = - | string | number | boolean | null + | 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 +Implement self-referential recursive type aliases directly. This drastically streamlines the definition of infinitely nested data structures like JSON trees or file system graphs. +## 🚨 39. `infer` keyword in conditional types > [!NOTE] > **Context:** Extracting internal types from complex structures. ### ❌ Bad Practice ```typescript -// Hardcoded extraction -type GetArrayType = T extends string[] ? string : never; +// Hardcoded manual extraction logic +type GetArrayType = T extends string[] ? string : + T extends number[] ? number : never; ``` ### ⚠️ Problem -Hardcoding extractions limits reusability and doesn't scale. +Hardcoding type extractions via chained conditionals lacks scalability, violates DRY principles, and becomes impossible to maintain for complex or infinite generic permutations. ### ✅ 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 +Utilize the `infer` keyword within conditional types. This commands the TypeScript compiler to dynamically unwrap, capture, and expose internal type structures autonomously. +## 🚨 40. Tuple types for fixed-length data > [!NOTE] > **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 +const [x, y, z] = point; // 'z' resolves to 'number', but is actually undefined at runtime! ``` ### ⚠️ Problem -`number[]` doesn't capture the length, leading to potential out-of-bounds errors during destructuring. +Standard array types (`T[]`) fail to enforce length or sequence, exposing downstream destructuring logic to critical out-of-bounds runtime errors. ### ✅ Best Practice ```typescript const point: [number, number] = [10, 20]; -// const [x, y, z] = point; // Error: Tuple has 2 elements +// const [x, y, z] = point; // TS Error: Tuple type '[number, number]' of length '2' has no element at index '2'. ``` ### 🚀 Solution -Use Tuples for arrays where the number of elements and their positions are fixed and meaningful. +Apply Tuple types (e.g., `[T, U]`) for array structures where exact length and positional semantics are deterministic boundaries.