HomeAbout Me

React Fundamental: TypeScript

By Daniel Nguyen
Published in React JS
May 06, 2025
2 min read
React Fundamental: TypeScript

🧠 Mastering TypeScript in React: From Red Squiggles to Confidence

When you’re building maintainable and scalable React applications, TypeScript is one of the most valuable tools in your developer toolbox. It offers safety, productivity, and clarity — even if it seems strict or frustrating at first.

But remember:

“TypeScript is not making your life terrible. It’s just showing you how terrible your life already is.”Kent C. Dodds

Let’s walk through how to use TypeScript effectively in React — from typing components and props, to improving developer experience with narrowing, deriving, and even satisfying types. 🦺


🚀 Why TypeScript?

Think of TypeScript as a brutally honest friend. It points out the problems before your users run into them — the “spinach in your teeth” of your codebase.

Once you get past the initial learning curve, TypeScript becomes a key ally in reducing bugs and making your codebase easier to navigate and refactor.


🛠️ Typing Functions

Let’s start with the basics: typing regular functions.

// JS function
function getName(user) {
return user.name ?? 'Unknown'
}
// With types
type User = { name?: string }
function getName(user: User): string {
return user.name ?? 'Unknown'
}

TypeScript will infer return types in many cases, but specifying them makes your code more readable and robust.

📘 Learn More: TypeScript Function Syntaxes


⚛️ Typing React Components

React components are just functions. That means you can type them the same way:

type MessageProps = { children: React.ReactNode }
function Message({ children }: MessageProps) {
return <div className="message">{children}</div>
}

You can also inline props or destructure them for conciseness:

function Message({ children }: { children: React.ReactNode }) {
return <div className="message">{children}</div>
}

📘 Resource: React + TypeScript Cheatsheets


➕ Narrowing Types for Safer Props

Let’s say we have a Calculator component:

<Calculator left={2} operator="**" right={3} /> // 💥 runtime error!

The problem? The operator prop is typed as string, which is too broad.

Instead, narrow it:

type Operator = '+' | '-' | '*' | '/'

Now TypeScript will complain if someone tries to use an unsupported operator.

type CalculatorProps = {
left: number
right: number
operator: Operator
}

This makes your component API safer and more discoverable.


🧪 Deriving Types with typeof and keyof

Avoid repeating yourself by deriving prop types directly from objects.

const operations = {
'+': (a: number, b: number) => a + b,
'-': (a: number, b: number) => a - b,
'*': (a: number, b: number) => a * b,
'/': (a: number, b: number) => a / b,
}
type Operator = keyof typeof operations

Now, if you add a new operation to the operations object, TypeScript automatically includes it in the Operator type.

📘 More on typeof 📘 More on keyof


⚙️ Setting Default Props

Want to make props optional with defaults?

type CalculatorProps = {
left?: number
right?: number
operator?: Operator
}
function Calculator({
left = 0,
right = 0,
operator = '+',
}: CalculatorProps) {
const result = operations[operator](left, right)
return <div>{result}</div>
}

Now, this is valid:

<Calculator /> // Renders 0 + 0 = 0

📘 Destructuring with Defaults


🔁 Reducing Duplication with Record

Instead of typing each operation function manually:

const operations = {
'+': (a: number, b: number) => a + b,
'-': (a: number, b: number) => a - b,
// ...
}

Use Record:

type OperationFn = (a: number, b: number) => number
type Operator = '+' | '-' | '*' | '/'
const operations: Record<Operator, OperationFn> = {
'+': (a, b) => a + b,
'-': (a, b) => a - b,
'*': (a, b) => a * b,
'/': (a, b) => a / b,
}

This avoids repeating function types, and helps TypeScript enforce structure.

📘 Record Utility Type


✅ Enforcing Correct Values with satisfies

Using Record works, but can sometimes widen types. You can fix that with satisfies:

const operations = {
'+': (a, b) => a + b,
'-': (a, b) => a - b,
'*': (a, b) => a * b,
'/': (a, b) => a / b,
} satisfies Record<Operator, OperationFn>

This ensures:

  • All required keys are present
  • Function signatures match
  • Types aren’t unnecessarily widened

📘 TypeScript satisfies operator


🧯 When TypeScript Gets in the Way

Sometimes, TypeScript can slow you down — and that’s OK. You can ask it to hush temporarily:

// @ts-expect-error I’ll fix this later
make.magic()

Don’t let the red squiggles keep you from making progress. TypeScript is here to help — not block you.


🧠 Wrap-Up

TypeScript makes your React applications more robust, self-documenting, and maintainable. Mastering it takes time, but step by step, you’ll gain confidence.

Here’s what you should walk away with:

  • ✅ Typing props is just like typing function arguments.
  • 🧠 Narrowing union types prevents runtime errors.
  • 🧪 typeof + keyof helps derive types without repetition.
  • 🧰 Record reduces redundancy.
  • satisfies enforces constraints without widening types.
  • ⛑️ You can still move forward with @ts-expect-error when needed.

📚 Keep these references handy:


💬 “You don’t have to get it perfect. Just get it going, and let TypeScript help guide you from there.”


Tags

#ReactFundamental

Share

Previous Article
React Fundamental: Custom components

Table Of Contents

1
🚀 Why TypeScript?
2
🛠️ Typing Functions
3
⚛️ Typing React Components
4
➕ Narrowing Types for Safer Props
5
🧪 Deriving Types with typeof and keyof
6
⚙️ Setting Default Props
7
🔁 Reducing Duplication with Record
8
✅ Enforcing Correct Values with satisfies
9
🧯 When TypeScript Gets in the Way
10
🧠 Wrap-Up

Related Posts

React Fundamental: Rendering Arrays
May 11, 2025
3 min
© 2025, All Rights Reserved.
Powered By

Quick Links

About Me

Legal Stuff

Social Media