TypeScript Lesson 11: TypeScript with React
React + TypeScript is the most popular frontend combination in professional development. Types make components self-documenting and catch prop errors instantly.
Setup
# Create a React + TypeScript project
npm create vite@latest my-react-ts -- --template react-ts
cd my-react-ts
npm install
npm run dev
Typing Props
// Define an interface for props
interface ButtonProps {
label: string;
onClick: () => void;
variant?: "primary" | "secondary" | "danger";
disabled?: boolean;
size?: "sm" | "md" | "lg";
}
// Use React.FC<Props> or just type the function parameter
const Button: React.FC<ButtonProps> = ({ label, onClick, variant = "primary", disabled = false }) => {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
disabled={disabled}
>
{label}
</button>
);
};
// Usage — TypeScript errors if you miss required props!
<Button label="Save" onClick={handleSave} />
<Button label="Delete" onClick={handleDelete} variant="danger" />
Typed useState and useRef
import { useState, useRef } from "react";
function SearchBox() {
const [query, setQuery] = useState<string>(""); // explicit
const [results, setResults] = useState<User[]>([]); // array of Users
const [selected, setSelected] = useState<User | null>(null);
const inputRef = useRef<HTMLInputElement>(null); // typed DOM ref
const focus = () => inputRef.current?.focus(); // safe optional chaining
return <input ref={inputRef} value={query} onChange={e => setQuery(e.target.value)} />;
}
Event Types
// Common React event types:
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); };
const handleKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") { ... }
};
🏋️ Practice Task
Build a TypeScript React component: UserCard that accepts: user: User (id, name, email, avatar?: string, role: “admin”|”user”), onEdit?: (user: User) => void, onDelete?: (id: number) => void. Show avatar (or initials if no avatar), name, email, role badge. Edit/Delete buttons only if handlers are passed.
💡 Hint: For initials: user.name.split(” “).map(n => n[0]).join(“”). Conditional button: {onEdit && }