Welcome back to the React Mastery Series!
In the previous article, we explored Production-Ready React Architecture and learned how enterprise applications are structured using:
- Feature-based architecture
- Service layers
- Custom Hooks
- State management
- Scalable folder organization
Today, we will explore one of the most important skills for modern React developers:
TypeScript with React
Most enterprise React applications today are built using:
React + TypeScript
because TypeScript helps developers write:
- Safer code
- More maintainable applications
- Better developer experiences
- Fewer runtime errors
What is TypeScript?
TypeScript is a programming language built on top of JavaScript.
It adds:
- Static typing
- Interfaces
- Generics
- Type checking
- Better IDE support
JavaScript:
let age = 30;
age = "thirty";
JavaScript allows this.
The error appears only at runtime.
TypeScript:
let age: number = 30;
age = "thirty";
Compiler error:
Type 'string' is not assignable to type 'number'
The problem is detected before running the application.
Why Use TypeScript with React?
Large React applications contain:
- Hundreds of components
- Multiple developers
- Complex data flows
- API integrations
Without types:
Component
|
↓
Unknown Data
|
↓
Runtime Error
With TypeScript:
Component
|
↓
Typed Data
|
↓
Compile-Time Validation
Creating a React TypeScript Project
Using Vite:
npm create vite@latest my-app
Select:
React
TypeScript
or:
npx create-react-app my-app --template typescript
TypeScript File Extensions
React projects use:
.js
.jsx
for JavaScript.
TypeScript uses:
.ts
.tsx
.tsx is used for React components.
Example:
Button.tsx
Dashboard.tsx
App.tsx
Typing React Components
JavaScript component:
function Welcome() {
return <h1>Hello</h1>;
}
TypeScript component:
function Welcome(): JSX.Element {
return <h1>Hello</h1>;
}
The return type tells TypeScript:
"This component returns JSX."
Typing Component Props
One of the most common TypeScript use cases.
JavaScript:
function User({ name, age }) {
return <h2>{name}</h2>;
}
Problem:
TypeScript does not know:
- What type is name?
- What type is age?
Create an interface:
interface UserProps {
name: string;
age: number;
}
Use it:
function User({name, age }: UserProps){
return ( <h2> {name} - {age} </h2>);
}
Now:
<User name="Siva" age={30} />
works.
Incorrect:
<User name={100} age="thirty" />
TypeScript catches the error.
Optional Props
Sometimes props are optional.
Example:
interface UserProps {
name: string;
age?: number;
}
Now:
<User name="Siva"/>
is valid.
Default Props with TypeScript
Example:
interface ButtonProps {
text?: string;
}
Component:
function Button({ text="Submit"}:ButtonProps){
return (<button> {text} </button>);
}
Typing useState
React state should also have types.
Without type:
const [count,setCount] = useState(0);
TypeScript automatically understands:
count → number
For complex values:
interface User {
id:number;
name:string;
email:string;
}
const [user,setUser] = useState<User | null>(null);
Meaning:
Initially:
user = null
Later:
user = User object
Typing Arrays
Example:
interface Product {
id:number;
name:string;
price:number;
}
const [products,setProducts] = useState<Product[]>([]);
Now TypeScript knows:
products is an array of Product
Typing Events
React events also need types.
Example:
Input change:
function handleChange(event: React.ChangeEvent<HTMLInputElement>){
console.log(event.target.value);
}
Usage:
<input onChange={handleChange} />
Button click:
function handleClick(event:React.MouseEvent<HTMLButtonElement>){
console.log("Clicked");
}
Typing Forms
Example:
const handleSubmit = (event:React.FormEvent<HTMLFormElement>)=> {
event.preventDefault();
}
Typing useRef
Example:
Accessing input element:
const inputRef = useRef<HTMLInputElement>(null);
Usage:
inputRef.current?.focus();
The ? protects against null values.
Typing useReducer
Earlier we learned:
State + Action = Reducer
With TypeScript:
State:
interface State {
count:number;
}
Action:
type Action = { type:"increment"} | {type:"decrement"};
Reducer:
function reducer(state:State, action:Action):State {
switch (action.type) {
case "increment":
return {
count: state.count + 1,
};
case "decrement":
return {
count: state.count - 1,
};
default:
return state;
}
}
Typing API Responses
Enterprise applications heavily depend on APIs.
Example response:
{
"id":101,
"name":"Siva",
"role":"Developer"
}
Create interface:
interface Employee {
id:number;
name:string;
role:string;
}
API call:
const response:Employee = await getEmployee();
Now components know exactly what data they receive.
Creating Reusable Generic Components
TypeScript becomes powerful with generics.
Example:
Reusable Table:
interface TableProps<T>{
data:T[];
}
Now the table can display:
Users:
<Table<User> data={users} />
Products:
<Table<Product> data={products} />
Same component.
Different data types.
Type vs Interface
Both can define structures.
Interface:
interface User {
name:string;
age:number;
}
Type:
type User = {
name:string;
age:number;
}
Common practice:
Use:
-
interfacefor objects -
typefor unions and complex types
Union Types
Example:
type Status = "loading" | "success" | "error";
Usage:
const status:Status="loading";
Only allowed values:
loading
success
error
React Context with TypeScript
Example:
interface AuthContextType {
user:User|null;
login:()=>void;
logout:()=>void;
}
Create context:
const AuthContext = createContext<AuthContextType | null>(null);
Now context usage becomes type-safe.
Enterprise Example: Banking Application
Imagine a transaction dashboard.
API response:
interface Transaction {
id:number;
amount:number;
date:string;
status: "SUCCESS"| "PENDING" | "FAILED";
}
Component:
function TransactionList({transactions}:{transactions:Transaction[]}) {
return (
<>
{transactions.map((transaction) => (
<div>{transaction.amount}</div>
))}
</>
);
}
Benefits:
- Developers know the data structure
- IDE provides autocomplete
- Errors are caught early
Common Mistakes
1. Using "any" Everywhere
Avoid:
let data:any;
Because it removes TypeScript benefits.
2. Ignoring Null Values
Incorrect:
user.name
when user can be null.
Correct:
user?.name
3. Over-typing Simple Values
Avoid unnecessary complexity.
Example:
const count:number = 0;
Usually:
const count = 0;
is enough.
TypeScript Best Practices
- Define interfaces near the feature where they are used.
- Avoid using
any. - Type API responses.
- Use reusable types.
- Use strict mode.
- Prefer meaningful type names.
- Keep types organized.
TypeScript Project Structure
A scalable approach:
src
├── types
│ ├── user.ts
│ ├── api.ts
│
├── features
│ └── transactions
│ ├── components
│ ├── services
│ └── types.ts
Key Takeaways
Today, we learned:
✅ TypeScript adds safety to React applications.
✅ Interfaces define component contracts.
✅ Props, state, events, and APIs can be strongly typed.
✅ Generics help create reusable components.
✅ TypeScript improves maintainability in enterprise projects.
✅ React + TypeScript is the preferred stack for modern applications.
Coming Next 🚀
In Day 22, we will explore:
React State Management with Redux Toolkit – Building Enterprise Applications
We will learn:
- Why Redux exists
- Redux architecture
- Store
- Actions
- Reducers
- Slices
- Redux Toolkit
- Async API calls
- Middleware
- Real-world enterprise examples
Redux Toolkit is one of the most widely used state management solutions in large-scale React applications.
Happy Coding! 🚀
Top comments (0)