Interactive Course

Learn TypeScript
with Next.js

5 lessons. Real code. A working Todo app at the end. Each lesson builds on the last.

Launch the App →or scroll down to learn
1

Types & Variables

Declaring what kind of data you have

stringnumberbooleantype inferencearrays
TypeScript
// Explicit types
let name: string = "Bella"
let age: number = 8
let available: boolean = true

// Type inference — TypeScript figures it out
let breed = "French Bulldog"  // inferred: string
let price = 1200              // inferred: number

// Arrays
let names: string[] = ["Bella", "Max"]
let prices: number[] = [800, 1200, 950]

// Union type — value must be one of these
type Status = "available" | "reserved" | "sold"
let status: Status = "available"
status = "lost"  // ❌ Error! Not in the union
💡

Unlike Java, TypeScript has one 'number' type for everything — int, float, double. The colon syntax declares the type.

2

Interfaces

Defining the shape of your objects

interfaceoptional ?readonlyOmit<>Record<>
TypeScript
interface Todo {
  id: string            // required
  text: string          // required
  completed: boolean    // required
  priority: Priority    // required
  createdAt: string     // required
}

interface Puppy {
  readonly id: number   // can't change after creation
  name: string
  description?: string  // ? = optional
}

// Omit removes fields from an existing interface
type NewTodo = Omit<Todo, "id" | "createdAt">
// NewTodo is: { text, completed, priority }

// Record<K, V> = typed object / map
const colors: Record<Priority, string> = {
  low: "green",
  medium: "yellow",
  high: "red",
}
💡

interface is like a Java POJO / model class — but lighter. No constructor, no getters/setters. TypeScript checks the shape at compile time.

3

Functions

Typing inputs, outputs, and event handlers

return typesvoidarrow functionsReact events
TypeScript
// Named function
function formatPrice(price: number): string {
  return price + " €"
}

// Arrow function — very common in React
const double = (n: number): number => n * 2

// void = returns nothing
const logMsg = (msg: string): void => {
  console.log(msg)
}

// React event types
const handleSubmit = (
  e: React.FormEvent<HTMLFormElement>
): void => {
  e.preventDefault()
  // ...
}

const handleChange = (
  e: React.ChangeEvent<HTMLInputElement>
): void => {
  setValue(e.target.value)
}
💡

The syntax after the closing ) is the return type. void means the function returns nothing — like Java void methods.

4

useState with Types

Typed React hooks

useState<T>type narrowinguseMemogenerics
TypeScript
import { useState, useMemo } from "react"

// Typed state hooks
const [todos, setTodos] = useState<Todo[]>([])
const [input, setInput] = useState<string>("")
const [filter, setFilter] = useState<FilterType>("all")

// Adding to state — 'prev' is automatically typed as Todo[]
const addTodo = (text: string): void => {
  setTodos((prev: Todo[]) => [
    { id: crypto.randomUUID(), text, completed: false },
    ...prev,
  ])
}

// useMemo — typed return value is inferred
const filtered = useMemo(
  () => todos.filter((t) => !t.completed),
  [todos]  // only recalculates when todos changes
)
💡

useState<Todo[]> tells TypeScript the state is an array of Todo. Without it, TypeScript infers 'never[]' which is useless.

5

Next.js App Router

Server vs Client components

use clientServer Componentpage.tsxlayout.tsxLink
TypeScript
// app/page.tsx — SERVER COMPONENT (default)
// Runs on the server. No useState allowed here.
// Good for: static content, data fetching, SEO

import Link from "next/link"

export default function HomePage() {
  return (
    <main>
      <h1>Hello from the server!</h1>
      <Link href="/todo">Go to Todo App</Link>
    </main>
  )
}

// ─────────────────────────────────────────────

// app/todo/page.tsx — CLIENT COMPONENT
// "use client" at the top — runs in the browser
// Can use: useState, useEffect, event handlers

"use client"
import { useState } from "react"

export default function TodoPage() {
  const [todos, setTodos] = useState<Todo[]>([])
  // ...
}
💡

This is what makes Next.js different from plain React. By default, every file is a Server Component. Add 'use client' only when you need state or events.

Ready to build?

All 5 concepts are used in the Todo app. Open it and study the code.

Open the Todo App →