~/logs/3
root@zourdy:~$ cat log_003.md
> status: [ACTIVE] [FEATURED: FALSE]
> category: FRONTEND
> views: 3,421
> status: [ACTIVE] [FEATURED: FALSE]
> category: FRONTEND
> views: 3,421
Frontend
> Advanced TypeScript Patterns for React Applications_
[ABSTRACT] Master advanced TypeScript patterns and techniques to build type-safe, maintainable React applications with better developer experience.
Michael Rodriguez|TypeScript Specialist
1/5/2024
15 min read
3,421 views
#TypeScript#React#Patterns#Type Safety
> Advanced TypeScript Patterns for React Applications
TypeScript has become essential for building robust React applications. Let's explore advanced patterns that will elevate your code quality and developer experience.
> Generic Components and Props
> Creating Flexible Generic Components
tsx.sh// Generic List Component interface ListProps<T> { items: T[] renderItem: (item: T, index: number) => React.ReactNode keyExtractor: (item: T) => string | number emptyMessage?: string className?: string } export function List<T>({ items, renderItem, keyExtractor, emptyMessage = "No items found", className = "" }: ListProps<T>) { if (items.length === 0) { return ( <div className={`text-gray-500 text-center py-8 ${className}`}> {emptyMessage} </div> ) } return ( <div className={className}> {items.map((item, index) => ( <div key={keyExtractor(item)}> {renderItem(item, index)} </div> ))} </div> ) } // Usage interface User { id: number name: string email: string } export function UserList({ users }: { users: User[] }) { return ( <List items={users} keyExtractor={(user) => user.id} renderItem={(user) => ( <div className="p-4 border rounded"> <h3>{user.name}</h3> <p>{user.email}</p> </div> )} emptyMessage="No users found" /> ) }
> Advanced Hook Patterns
> Custom Hook with Generic Return Types
tsx.sh// Generic API Hook interface UseApiResult<T> { data: T | null loading: boolean error: string | null refetch: () => Promise<void> } export function useApi<T>(url: string): UseApiResult<T> { const [data, setData] = useState<T | null>(null) const [loading, setLoading] = useState(true) const [error, setError] = useState<string | null>(null) const fetchData = useCallback(async () => { try { setLoading(true) setError(null) const response = await fetch(url) if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } const result = await response.json() setData(result) } catch (err) { setError(err instanceof Error ? err.message : 'An error occurred') } finally { setLoading(false) } }, [url]) useEffect(() => { fetchData() }, [fetchData]) return { data, loading, error, refetch: fetchData } } // Usage with specific types interface Post { id: number title: string content: string author: string } export function PostList() { const { data: posts, loading, error } = useApi<Post[]>('/api/posts') if (loading) return <div>Loading...</div> if (error) return <div>Error: {error}</div> return ( <List items={posts || []} keyExtractor={(post) => post.id} renderItem={(post) => ( <article className="p-6 border rounded-lg"> <h2 className="text-xl font-bold">{post.title}</h2> <p className="text-gray-600">By {post.author}</p> <p className="mt-2">{post.content}</p> </article> )} /> ) }
> Discriminated Unions for State Management
> Type-Safe State Patterns
tsx.sh// Define state types using discriminated unions type AsyncState<T> = | { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: T } | { status: 'error'; error: string } // Reducer for async operations type AsyncAction<T> = | { type: 'FETCH_START' } | { type: 'FETCH_SUCCESS'; payload: T } | { type: 'FETCH_ERROR'; payload: string } | { type: 'RESET' } function asyncReducer<T>( state: AsyncState<T>, action: AsyncAction<T> ): AsyncState<T> { switch (action.type) { case 'FETCH_START': return { status: 'loading' } case 'FETCH_SUCCESS': return { status: 'success', data: action.payload } case 'FETCH_ERROR': return { status: 'error', error: action.payload } case 'RESET': return { status: 'idle' } default: return state } } // Custom hook using the reducer export function useAsyncData<T>(fetchFn: () => Promise<T>) { const [state, dispatch] = useReducer(asyncReducer<T>, { status: 'idle' }) const execute = useCallback(async () => { dispatch({ type: 'FETCH_START' }) try { const data = await fetchFn() dispatch({ type: 'FETCH_SUCCESS', payload: data }) } catch (error) { dispatch({ type: 'FETCH_ERROR', payload: error instanceof Error ? error.message : 'Unknown error' }) } }, [fetchFn]) const reset = useCallback(() => { dispatch({ type: 'RESET' }) }, []) return { state, execute, reset } }
> Advanced Component Patterns
> Render Props with TypeScript
tsx.sh// Render props pattern with strong typing interface RenderPropsComponentProps<T> { data: T[] children: (props: { items: T[] selectedItem: T | null selectItem: (item: T) => void clearSelection: () => void }) => React.ReactNode } export function SelectableList<T extends { id: string | number }>({ data, children }: RenderPropsComponentProps<T>) { const [selectedItem, setSelectedItem] = useState<T | null>(null) const selectItem = useCallback((item: T) => { setSelectedItem(item) }, []) const clearSelection = useCallback(() => { setSelectedItem(null) }, []) return ( <> {children({ items: data, selectedItem, selectItem, clearSelection })} </> ) } // Usage interface Product { id: number name: string price: number } export function ProductSelector({ products }: { products: Product[] }) { return ( <SelectableList data={products}> {({ items, selectedItem, selectItem, clearSelection }) => ( <div className="space-y-4"> <div className="grid grid-cols-1 md:grid-cols-3 gap-4"> {items.map((product) => ( <button key={product.id} onClick={() => selectItem(product)} className={`p-4 border rounded-lg transition-colors ${ selectedItem?.id === product.id ? 'border-blue-500 bg-blue-50' : 'border-gray-200 hover:border-gray-300' }`} > <h3 className="font-semibold">{product.name}</h3> <p className="text-gray-600">${product.price}</p> </button> ))} </div> {selectedItem && ( <div className="p-4 bg-green-50 border border-green-200 rounded-lg"> <h4 className="font-semibold">Selected: {selectedItem.name}</h4> <p>Price: ${selectedItem.price}</p> <button onClick={clearSelection} className="mt-2 px-3 py-1 bg-red-500 text-white rounded" > Clear Selection </button> </div> )} </div> )} </SelectableList> ) }
> Type Guards and Utility Types
> Custom Type Guards
tsx.sh// Type guards for runtime type checking interface User { id: number name: string email: string role: 'admin' | 'user' } interface AdminUser extends User { role: 'admin' permissions: string[] } // Type guard functions export function isUser(value: unknown): value is User { return ( typeof value === 'object' && value !== null && 'id' in value && 'name' in value && 'email' in value && 'role' in value && typeof (value as User).id === 'number' && typeof (value as User).name === 'string' && typeof (value as User).email === 'string' && ['admin', 'user'].includes((value as User).role) ) } export function isAdminUser(user: User): user is AdminUser { return user.role === 'admin' && 'permissions' in user } // Usage in components export function UserProfile({ userData }: { userData: unknown }) { if (!isUser(userData)) { return <div>Invalid user data</div> } return ( <div className="p-6 border rounded-lg"> <h2 className="text-xl font-bold">{userData.name}</h2> <p className="text-gray-600">{userData.email}</p> <p className="text-sm text-gray-500">Role: {userData.role}</p> {isAdminUser(userData) && ( <div className="mt-4"> <h3 className="font-semibold">Admin Permissions:</h3> <ul className="list-disc list-inside"> {userData.permissions.map((permission) => ( <li key={permission}>{permission}</li> ))} </ul> </div> )} </div> ) }
> Conclusion
These advanced TypeScript patterns provide the foundation for building robust, type-safe React applications. By leveraging generics, discriminated unions, and proper type guards, you can create more maintainable and reliable code that scales with your application's complexity.
RELATED.LOGS
root@zourdy:~$ ls -la related_logs/
> found 1 related entries
> found 1 related entries
TABLE.OF.CONTENTS
TOC: ACTIVE