From const to Fetch: 10 Modern TypeScript Patterns Every React Engineer Must Master
TL;DR – Whether you’re about to dive into React or leveling‑up an existing codebase, these ten “building‑block” patterns will keep your apps type‑safe, expressive, and production‑ready. We’ll walk through real
.tsfiles (⚡ just like a lab notebook) and end with a Giphy-powered fetch demo you can copy‑paste into any React 19 project.
1 const, let, and the Death of var
// 01-const-let.ts
const firstName: string = 'Cristian';
let diceNumber = 6; // can be reassigned
// var oldSchool = true; // avoid: leaks to function scope
-
const⇒ read‑only reference (not necessarily immutable data). -
let⇒ block‑scoped variable—use when the value must change. -
Never use
var—it hoists unpredictably and breaks module isolation.
React tip: Declare component constants (e.g. default props, route paths) at the top level with
constso they’re hoisted and memo‑friendly.
2 Template Literals – No More String Math
// 02-template-string.ts
const user = 'Fer';
const repo = 42;
const uri = `https://api.github.com/users/${user}/repos/${repo}`;
Why you care:
- Safer than string concatenation (
typeofstaysstring). - Works inside JSX:
<img alt={Avatar of ${user}} … />.
3 Object Literals + Interfaces – Talk to TypeScript
// 03-object-literal.ts
interface Hero {
id: string;
displayName?: string; // optional
power: string[];
}
const ironman: Hero = {
id: 'IM-01',
power: ['repulsor', 'AI-assistant']
};
Why interfaces over inline types?
- Reusable across files (
export interface Hero…). - Extensible via declaration merging (
interface Hero { suitColor: string }). - Automatically documented in VS Code IntelliSense.
4 Arrays & Iteration – map, filter, reduce
// 04-arrays.ts
const powers = ['speed', 'flight', 'invisibility'] as const;
const withI = powers.filter(p => p.includes('i')); // ['flight', 'invisibility']
React tip: Pair
mapwith stable keys in a list component:
powers.map(p => <Chip key={p} label={p} />).
5 Functions: From Simple to “Return‑Many”
// 05-functions.ts
export function rollDice(): [number, boolean] {
const value = Math.floor(Math.random() * 6) + 1;
const lucky = value === 6;
return [value, lucky]; // tuple: number & boolean
}
- Return tuples when order matters (
[x, y]) and destructure on call. - Use function overloads for variants; keep implementation private.
6 Object Destructuring – Readable Parameter Lists
// 06-obj-destructuring.ts
function printHero({ id, power }: Hero) {
console.log(`#${id} → ${power.join(', ')}`);
}
In React this shines when you pass only what’s needed:
const { power, ...rest } = hero;
<PowerMeter value={power.length} />
<HeroCard {...rest} />
7 Array Destructuring – Skip by Position
// 07-array-destructuring.ts
const [first, , third] = ['a', 'b', 'c']; // first='a', third='c'
Common in hooks:
const [count, setCount] = useState(0);
8 Modules: import / export Without Tears
// 08-imp-exp.ts
export const AVATAR_URL = 'https://i.pravatar.cc/150?u=';
export default function avatar(id: string) {
return `${AVATAR_URL}${id}`;
}
// elsewhere
import avatar, { AVATAR_URL } from './avatar';
Rule of thumb:
- One default export per file for the “main” thing.
- Named exports for helpers & enums.
9 Promises – The Two Official Spellings
// 09-promises.ts
function dicePromise(): Promise<number> {
return new Promise(res => {
setTimeout(() => res(Math.floor(Math.random()*6)+1), 500);
});
}
dicePromise().then(console.log).catch(console.error);
Avoid the “pyramid of doom” with async/await (next section).
10 Fetch + Async/Await with the Giphy API
// 10-fetch-api.ts
interface GifResponse {
data: { images: { downsized: { url: string } } }[];
}
export async function getRandomGif(tag: string): Promise<string> {
const apiKey = import.meta.env.VITE_GIPHY_KEY;
const res = await fetch(
`https://api.giphy.com/v1/gifs/random?api_key=${apiKey}&tag=${tag}`
);
if (!res.ok) throw new Error('Giphy error');
const json = (await res.json()) as GifResponse;
return json.data.images.downsized.url;
}
How to use in React
import { useEffect, useState } from 'react';
export function GifCard({ tag }: { tag: string }) {
const [url, setUrl] = useState<string>();
useEffect(() => {
getRandomGif(tag).then(setUrl).catch(console.error);
}, [tag]);
return url ? <img src={url} alt={tag} /> : <p>Loading...</p>;
}
- Type the promise return so consumers get autocomplete for image URLs.
- Pull your API key from Vite environment variables (
VITE_*).
Cheat‑Sheet: Which Tool for Which Problem?
| Need | Use |
|---|---|
| Immutable references | const |
| Mutable loop index | let |
| Multi-line interpolation | Template literals |
| Shared shape across files | interface |
| Map over data in JSX | array.map |
| Multiple values back | Tuple returns |
| Reusable stateful logic | Custom React hook |
| Async HTTP |
fetch + async/await
|
| Compile‑time constants |
enum or as const array |
Final Thoughts
Great React apps aren’t built with components alone—they’re forged from thousands of tiny language habits that compound:
- Choosing the right declaration (
constvslet) keeps state predictable. - Typing objects and return values unlocks IntelliSense and refactor‑proofing.
- Mastering
fetchwith proper response interfaces prevents runtime 500s.
Commit these patterns to muscle memory before you dive into higher‑order hooks, state machines, or fancy animation libraries, and you’ll code like a scientific React‑TypeScript meta‑engineer every single day.
Happy hacking—and may your API calls always resolve! 🚀
✍️ Written by: Cristian Sifuentes – Full-stack dev crafting scalable apps with [NET - Azure], [Angular - React], Git, SQL & extensions. Clean code, dark themes, atomic commits
Tags: react19, typescript, javascript, async-await, state-management, frontend

Top comments (0)