TypeScript Patterns That Eliminate Runtime Bugs
Advanced TypeScript techniques I use daily — discriminated unions, branded types, and template literals — that catch entire classes of bugs at compile time.
Cutting through the noise — the React performance techniques that move the needle in real apps, and the ones that are mostly cargo cult.
Or rather, it's right but applied to the wrong problems. I see teams wrapping every component in React.memo, throwing useMemo on every value, and useCallback on every function — then wondering why their app is still slow.
The actual performance problems in React apps are almost never "too many re-renders of leaf components". They're data fetching waterfalls, layout thrashing, and bundle size.
The most common performance killer: sequential data fetching. Component A fetches user data, then renders Component B, which fetches permissions, which renders Component C that fetches the actual content.
Fix: fetch everything in parallel at the route level. With React Query:
function useDashboardData(userId: string) {
return useQueries({
queries: [
{ queryKey: ['user', userId], queryFn: () => fetchUser(userId) },
{ queryKey: ['permissions', userId], queryFn: () => fetchPermissions(userId) },
{ queryKey: ['content', userId], queryFn: () => fetchContent(userId) },
],
});
}
Three requests fire in parallel. The dashboard renders when all three resolve instead of waiting sequentially.
If you're rendering more than 50-100 items in a list, virtualize. Only render what's visible:
import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualList({ items }: { items: Item[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 72,
});
return (
<div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}>
<div style={{ height: `${virtualizer.getTotalSize()}px`, position: 'relative' }}>
{virtualizer.getVirtualItems().map((virtualItem) => (
<div
key={virtualItem.key}
style={{
position: 'absolute',
top: 0,
transform: `translateY(${virtualItem.start}px)`,
}}
>
<Item data={items[virtualItem.index]} />
</div>
))}
</div>
</div>
);
}
Going from rendering 500 DOM nodes to 10 visible ones is a 50x improvement. No amount of memoization beats this.
Ship only the JavaScript each route actually needs:
const BlogPage = lazy(() => import('./pages/BlogPage'));
const BlogPostPage = lazy(() => import('./pages/BlogPostPage'));
function App() {
return (
<Suspense fallback={<PageLoader />}>
<Routes>
<Route path="/blog" element={<BlogPage />} />
<Route path="/blog/:slug" element={<BlogPostPage />} />
</Routes>
</Suspense>
);
}
For a portfolio with a blog, the blog reader code doesn't need to load on the homepage.
useMemo is worth it when: the computation is provably expensive (sort/filter over 1000+ items), and the deps rarely change.
const filteredPosts = useMemo(
() =>
blogPosts
.filter((post) =>
post.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
post.tags.some((tag) => tag.toLowerCase().includes(searchQuery.toLowerCase()))
)
.filter((post) => activeCategory === 'All' || post.category === activeCategory),
[blogPosts, searchQuery, activeCategory]
);
This is the right use. Filtering an array of objects on every keystroke with useMemo makes sense. Memoizing a string concatenation does not.
Profile first. Use Chrome DevTools Performance tab or React DevTools Profiler. Fix what the profiler shows, not what you assume is slow.
Measure, don't guess.