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
.ts
files (⚡ 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
const
so 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 (
typeof
staysstring
). - 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
map
with 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 (
const
vslet
) keeps state predictable. - Typing objects and return values unlocks IntelliSense and refactor‑proofing.
- Mastering
fetch
with 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)