# TypeScript Conventions This document outlines our TypeScript conventions for the project. ## Configuration We use strict TypeScript settings to ensure type safety: ```json { "compilerOptions": { "strict": true, "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true, "strictBindCallApply": true, "strictPropertyInitialization": true, "noImplicitThis": true, "alwaysStrict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true } } ``` ## Naming Conventions ### Files and Directories - Use kebab-case for file and directory names: `product-list.tsx`, `view-model.ts` - Exception: React components can use PascalCase: `ProductCard.tsx` ### Variables and Functions - Use camelCase for variables and functions: `productList`, `fetchProducts` - Use PascalCase for React components and types: `ProductList`, `ProductDto` - Use ALL_CAPS for constants: `MAX_ITEMS`, `API_URL` ### Effector Naming - Prefix store names with `$`: `$products`, `$isLoading` - Suffix effect names with `Fx`: `fetchProductsFx`, `saveUserFx` - Use descriptive names for events: `setProductFilter`, `resetForm` ## Type Definitions ### Interfaces vs. Types - Use `interface` for object shapes that might be extended - Use `type` for unions, intersections, and simple object shapes ```typescript // Interface for extendable objects interface User { id: string; name: string; } interface AdminUser extends User { permissions: string[]; } // Type for unions and simple objects type Status = 'idle' | 'loading' | 'success' | 'error'; type ProductFilter = { category?: string; minPrice?: number; maxPrice?: number; }; ``` ### API Models - Use the generated types from the API client - Create wrapper types when needed for additional properties ```typescript import type { productCompositeDto } from '@lib/api/merchant/models/productCompositeDto'; // Extended type with UI-specific properties type ProductWithUIState = productCompositeDto & { isSelected: boolean; isExpanded: boolean; }; ``` ## React Component Types ### Functional Components ```typescript import React from 'react'; interface ButtonProps { label: string; onClick: () => void; disabled?: boolean; } export const Button: React.FC = ({ label, onClick, disabled = false }) => { return ( ); }; ``` ### Event Handlers ```typescript // Form event const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); // ... }; // Input change const handleChange = (e: React.ChangeEvent) => { const { name, value } = e.target; // ... }; // Click event const handleClick = (e: React.MouseEvent) => { // ... }; ``` ## Effector Types ### Events ```typescript import { createEvent } from 'effector'; // Event with no payload export const resetFilter = createEvent(); // Event with payload export const setSearchQuery = createEvent(); // Event with complex payload export const updateFilter = createEvent<{ category?: string; minPrice?: number; maxPrice?: number; }>(); ``` ### Stores ```typescript import { createStore } from 'effector'; // Define the store state type interface FilterState { searchQuery: string; category: string | null; minPrice: number | null; maxPrice: number | null; } // Initial state const initialState: FilterState = { searchQuery: '', category: null, minPrice: null, maxPrice: null }; // Create the store export const $filter = createStore(initialState); ``` ### Effects ```typescript import { createEffect } from 'effector'; import { ProductsViewsService } from '@lib/api/merchant/services/ProductsViewsService'; import type { productQueryRequestDto } from '@lib/api/merchant/models/productQueryRequestDto'; import type { productCompositeDto } from '@lib/api/merchant/models/productCompositeDto'; // Define effect with explicit parameter and return types export const fetchProductsFx = createEffect< productQueryRequestDto, productCompositeDto[], Error >(async (filter) => { const response = await ProductsViewsService.postApiProductCompositeQueryByCanonical({ requestBody: filter }); return Array.isArray(response) ? response : [response]; }); ``` ## Best Practices 1. **Avoid `any`**: Never use the `any` type. Use `unknown` if the type is truly unknown. 2. **Use type inference**: Let TypeScript infer types when possible, but add explicit types for function parameters and return values. 3. **Null vs. undefined**: Use `undefined` for optional values, `null` for intentionally absent values. 4. **Non-null assertion**: Avoid using the non-null assertion operator (`!`) when possible. Use optional chaining (`?.`) and nullish coalescing (`??`) instead. 5. **Type guards**: Use type guards to narrow types: ```typescript function isProduct(item: unknown): item is productCompositeDto { return ( typeof item === 'object' && item !== null && 'id' in item ); } ``` 6. **Readonly**: Use `readonly` for immutable properties: ```typescript interface Config { readonly apiUrl: string; readonly timeout: number; } ``` 7. **Generics**: Use generics for reusable components and functions: ```typescript function fetchData(url: string): Promise { return fetch(url).then(res => res.json()); } const data = await fetchData('/api/users'); ```