Skip to content
intermediatePhase 35 · TypeScript

TypeScript with React

Type React components, props, hooks, and event handlers with TypeScript.

1h
0 problems
Topic Progress0%

Typing Components

Typing Components

Function Component

import React from "react";

// Method 1: React.FC (not recommended)
const greeting: React.FC<{ name: string }> = ({ name }) => {
  return <h1>Hello, {name}!</h1>;
};

// Method 2: Props type (recommended)
type GreetingProps = {
  name: string;
};

function Greeting({ name }: GreetingProps) {
  return <h1>Hello, {name}!</h1>;
}

// Method 3: Inline type
function Greeting2({ name }: { name: string }) {
  return <h1>Hello, {name}!</h1>;
}

Component with Children

type CardProps = {
  title: string;
  children: React.ReactNode;
};

function Card({ title, children }: CardProps) {
  return (
    <div className="card">
      <h2>{title}</h2>
      <div className="content">{children}</div>
    </div>
  );
}

// Usage
<Card title="My Card">
  <p>This is card content</p>
</Card>

Default Props

type ButtonProps = {
  label: string;
  variant?: "primary" | "secondary";
  size?: "sm" | "md" | "lg";
};

function Button({ label, variant = "primary", size = "md" }: ButtonProps) {
  return (
    <button className={`btn btn-${variant} btn-${size}`}>
      {label}
    </button>
  );
}

Typing Props

Typing Props

Union Props

type TextProps = {
  as: "h1" | "h2" | "h3" | "p";
  children: React.ReactNode;
};

function Text({ as: Component, children }: TextProps) {
  return <Component>{children}</Component>;
}

// Usage
<Text as="h1">Title</Text>
<Text as="p">Paragraph</Text>

Props with Generics

type ListProps<T> = {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
};

function List<T>({ items, renderItem }: ListProps<T>) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{renderItem(item)}</li>
      ))}
    </ul>
  );
}

// Usage
<List
  items={["apple", "banana", "cherry"]}
  renderItem={(item) => item.toUpperCase()}
/>

Extending HTML Props

type CustomInputProps = {
  label: string;
  error?: string;
} & React.InputHTMLAttributes<HTMLInputElement>;

function CustomInput({ label, error, ...props }: CustomInputProps) {
  return (
    <div>
      <label>{label}</label>
      <input {...props} />
      {error && <span className="error">{error}</span>}
    </div>
  );
}

// Usage
<CustomInput
  label="Email"
  type="email"
  placeholder="Enter email"
  error="Invalid email"
/>

Typing Hooks

Typing Hooks

useState

// Basic
const [count, setCount] = useState(0); // inferred as number

// Explicit type
const [name, setName] = useState<string>("");

// Complex state
interface User {
  id: number;
  name: string;
  email: string;
}

const [user, setUser] = useState<User | null>(null);

// Union state
type Status = "idle" | "loading" | "error" | "success";
const [status, setStatus] = useState<Status>("idle");

useRef

// DOM reference
const inputRef = useRef<HTMLInputElement>(null);

// Mutable value (not DOM)
const countRef = useRef<number>(0);

// With initial value
const timerRef = useRef<NodeJS.Timeout | null>(null);

useContext

interface AuthContextType {
  user: User | null;
  login: (email: string, password: string) => Promise<void>;
  logout: () => void;
}

const AuthContext = React.createContext<AuthContextType | undefined>(undefined);

function useAuth(): AuthContextType {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error("useAuth must be used within AuthProvider");
  }
  return context;
}

useReducer

interface State {
  count: number;
}

type Action =
  | { type: "increment" }
  | { type: "decrement" }
  | { type: "reset" };

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    case "reset":
      return { count: 0 };
  }
}

const [state, dispatch] = useReducer(reducer, { count: 0 });

Typing Events

Typing Events

React Event Types

// Mouse events
function handleClick(e: React.MouseEvent<HTMLButtonElement>) {
  console.log(e.clientX, e.clientY);
}

// Keyboard events
function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
  console.log(e.key);
}

// Form events
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
  e.preventDefault();
  // handle submission
}

// Change events
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
  console.log(e.target.value);
}

Typed Event Handlers

type InputChangeEvent = React.ChangeEvent<HTMLInputElement>;
type FormSubmitEvent = React.FormEvent<HTMLFormElement>;

function SearchForm() {
  const [query, setQuery] = useState("");

  const handleChange = (e: InputChangeEvent) => {
    setQuery(e.target.value);
  };

  const handleSubmit = (e: FormSubmitEvent) => {
    e.preventDefault();
    console.log("Searching:", query);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" value={query} onChange={handleChange} />
      <button type="submit">Search</button>
    </form>
  );
}

Generic Event Components

type EventHandler<T> = (event: T) => void;

type ClickableProps = {
  onClick: EventHandler<React.MouseEvent<HTMLDivElement>>;
  children: React.ReactNode;
};

function Clickable({ onClick, children }: ClickableProps) {
  return <div onClick={onClick}>{children}</div>;
}

Practice Problems

0/3solved
Build TypeScript with React Component

Create a reusable React component implementing TypeScript with React. Include proper state management and accessibility.

Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where needed
TypeScript with React Testing

Write unit and integration tests for TypeScript with React using React Testing Library.

Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility tests
TypeScript with React Performance

Optimize TypeScript with React for performance. Consider memoization, code splitting, and bundle size.

Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysis

Quiz

1. How do you type a React component's props?

Question 1 options

2. What type represents children in React?

Question 2 options

3. How do you type a click event?

Question 3 options

4. How do you type useState with complex state?

Question 4 options

Flashcards

Question

How do you type a React component?

Answer

function Component({ prop }: PropType) or React.FC<PropType>

Question

What type should you use for children?

Answer

React.ReactNode

Question

How do you type a form event?

Answer

React.FormEvent<HTMLFormElement>

Question

How do you type a DOM ref?

Answer

useRef<HTMLElementType>(null)

Question

What is TypeScript with React?

Answer

TypeScript with React is a key concept in frontend development.

Revision Notes

Key Takeaways

  • 1.Function components use props type annotation
  • 2.React.ReactNode for children props
  • 3.Event handlers use React-specific event types
  • 4.useRef needs generic type for DOM elements
  • 5.Extend HTML attributes for custom components

Interview Tips

  • Show how to type functional components with props
  • Demonstrate typed event handlers
  • Explain how to type hooks correctly

Cheat Sheet

Cheat Sheet

Component Props

type Props = { name: string; age?: number };
function Comp({ name, age }: Props) { ... }

Children

type Props = { children: React.ReactNode };

Events

onClick: (e: React.MouseEvent<HTMLButtonElement>) => void
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void

Hooks

const [val, setVal] = useState<string>("");
const ref = useRef<HTMLInputElement>(null);
const ctx = useContext(MyContext);

Extending HTML Props

type Props = { label: string } & React.InputHTMLAttributes<HTMLInputElement>;