# TypeScript Standards TypeScript best practices for type safety and maintainability in React frontend code. --- ## Strict Mode ### Configuration TypeScript strict mode is **enabled** in the project: ```json // tsconfig.json { "compilerOptions": { "strict": true, "noImplicitAny": true, "strictNullChecks": true } } ``` **This means:** - No implicit `any` types - Null/undefined must be handled explicitly - Type safety enforced --- ## No `any` Type ### The Rule ```typescript // ❌ NEVER use any function handleData(data: any) { return data.something; } // ✅ Use specific types interface MyData { something: string; } function handleData(data: MyData) { return data.something; } // ✅ Or use unknown for truly unknown data function handleUnknown(data: unknown) { if (typeof data === 'object' && data !== null && 'something' in data) { return (data as MyData).something; } } ``` **If you truly don't know the type:** - Use `unknown` (forces type checking) - Use type guards to narrow - Document why type is unknown --- ## Explicit Return Types ### Function Return Types ```typescript // ✅ CORRECT - Explicit return type function getUser(id: number): Promise { return apiClient.get(`/users/${id}`); } function calculateTotal(items: Item[]): number { return items.reduce((sum, item) => sum + item.price, 0); } // ❌ AVOID - Implicit return type (less clear) function getUser(id: number) { return apiClient.get(`/users/${id}`); } ``` ### Component Return Types ```typescript // React.FC already provides return type (ReactElement) export const MyComponent: React.FC = ({ prop }) => { return
{prop}
; }; // For custom hooks function useMyData(id: number): { data: Data; isLoading: boolean } { const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(true); return { data: data!, isLoading }; } ``` --- ## Type Imports ### Use 'type' Keyword ```typescript // ✅ CORRECT - Explicitly mark as type import import type { User } from '~types/user'; import type { Post } from '~types/post'; import type { SxProps, Theme } from '@mui/material'; // ❌ AVOID - Mixed value and type imports import { User } from '~types/user'; // Unclear if type or value ``` **Benefits:** - Clearly separates types from values - Better tree-shaking - Prevents circular dependencies - TypeScript compiler optimization --- ## Component Prop Interfaces ### Interface Pattern ```typescript /** * Props for MyComponent */ interface MyComponentProps { /** The user ID to display */ userId: number; /** Optional callback when action completes */ onComplete?: () => void; /** Display mode for the component */ mode?: 'view' | 'edit'; /** Additional CSS classes */ className?: string; } export const MyComponent: React.FC = ({ userId, onComplete, mode = 'view', // Default value className, }) => { return
...
; }; ``` **Key Points:** - Separate interface for props - JSDoc comments for each prop - Optional props use `?` - Provide defaults in destructuring ### Props with Children ```typescript interface ContainerProps { children: React.ReactNode; title: string; } // React.FC automatically includes children type, but be explicit export const Container: React.FC = ({ children, title }) => { return (

{title}

{children}
); }; ``` --- ## Utility Types ### Partial ```typescript // Make all properties optional type UserUpdate = Partial; function updateUser(id: number, updates: Partial) { // updates can have any subset of User properties } ``` ### Pick ```typescript // Select specific properties type UserPreview = Pick; const preview: UserPreview = { id: 1, name: 'John', email: 'john@example.com', // Other User properties not allowed }; ``` ### Omit ```typescript // Exclude specific properties type UserWithoutPassword = Omit; const publicUser: UserWithoutPassword = { id: 1, name: 'John', email: 'john@example.com', // password and passwordHash not allowed }; ``` ### Required ```typescript // Make all properties required type RequiredConfig = Required; // All optional props become required ``` ### Record ```typescript // Type-safe object/map const userMap: Record = { 'user1': { id: 1, name: 'John' }, 'user2': { id: 2, name: 'Jane' }, }; // For styles import type { SxProps, Theme } from '@mui/material'; const styles: Record> = { container: { p: 2 }, header: { mb: 1 }, }; ``` --- ## Type Guards ### Basic Type Guards ```typescript function isUser(data: unknown): data is User { return ( typeof data === 'object' && data !== null && 'id' in data && 'name' in data ); } // Usage if (isUser(response)) { console.log(response.name); // TypeScript knows it's User } ``` ### Discriminated Unions ```typescript type LoadingState = | { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: Data } | { status: 'error'; error: Error }; function Component({ state }: { state: LoadingState }) { // TypeScript narrows type based on status if (state.status === 'success') { return ; // data available here } if (state.status === 'error') { return ; // error available here } return ; } ``` --- ## Generic Types ### Generic Functions ```typescript function getById(items: T[], id: number): T | undefined { return items.find(item => (item as any).id === id); } // Usage with type inference const users: User[] = [...]; const user = getById(users, 123); // Type: User | undefined ``` ### Generic Components ```typescript interface ListProps { items: T[]; renderItem: (item: T) => React.ReactNode; } export function List({ items, renderItem }: ListProps): React.ReactElement { return (
{items.map((item, index) => (
{renderItem(item)}
))}
); } // Usage items={users} renderItem={(user) => } /> ``` --- ## Type Assertions (Use Sparingly) ### When to Use ```typescript // ✅ OK - When you know more than TypeScript const element = document.getElementById('my-element') as HTMLInputElement; const value = element.value; // ✅ OK - API response that you've validated const response = await api.getData(); const user = response.data as User; // You know the shape ``` ### When NOT to Use ```typescript // ❌ AVOID - Circumventing type safety const data = getData() as any; // WRONG - defeats TypeScript // ❌ AVOID - Unsafe assertion const value = unknownValue as string; // Might not actually be string ``` --- ## Null/Undefined Handling ### Optional Chaining ```typescript // ✅ CORRECT const name = user?.profile?.name; // Equivalent to: const name = user && user.profile && user.profile.name; ``` ### Nullish Coalescing ```typescript // ✅ CORRECT const displayName = user?.name ?? 'Anonymous'; // Only uses default if null or undefined // (Different from || which triggers on '', 0, false) ``` ### Non-Null Assertion (Use Carefully) ```typescript // ✅ OK - When you're certain value exists const data = queryClient.getQueryData(['data'])!; // ⚠️ CAREFUL - Only use when you KNOW it's not null // Better to check explicitly: const data = queryClient.getQueryData(['data']); if (data) { // Use data } ``` --- ## Summary **TypeScript Checklist:** - ✅ Strict mode enabled - ✅ No `any` type (use `unknown` if needed) - ✅ Explicit return types on functions - ✅ Use `import type` for type imports - ✅ JSDoc comments on prop interfaces - ✅ Utility types (Partial, Pick, Omit, Required, Record) - ✅ Type guards for narrowing - ✅ Optional chaining and nullish coalescing - ❌ Avoid type assertions unless necessary **See Also:** - [component-patterns.md](component-patterns.md) - Component typing - [data-fetching.md](data-fetching.md) - API typing