← Back to TypeScript Course | Chapter 28: Real World Projects | Lesson 1 of 14

Todo App with TypeScript

A Todo application is a practical way to combine interfaces, classes or functions, arrays, events, and DOM APIs. It demonstrates how TypeScript improves everyday application code.

Core Concept

A typed todo app is a common first TypeScript project because its data model is small (an id, a text string, a completed flag) but still demonstrates end-to-end typing: typed state, typed props, and typed event handlers all in one place.

Example: Core Concept

typescript
interface Todo {
  id: string;
  text: string;
  completed: boolean;
}
const todo: Todo = { id: "1", text: "Buy milk", completed: false };
console.log(todo);

Basic Setup

A basic setup defines an interface Todo { id: string; text: string; completed: boolean } and a typed array Todo[] as the app's central piece of state, whether managed with useState, a class field, or a store.

Example: Basic Setup

typescript
interface Todo {
  id: string;
  text: string;
  completed: boolean;
}
const todos: Todo[] = [];
console.log(todos.length);

Typed Example

A typed example: function addTodo(text: string): Todo { return { id: crypto.randomUUID(), text, completed: false } } guarantees every new todo object always has the exact right shape, with no missing fields possible.

Example: Typed Example

typescript
interface Todo {
  id: string;
  text: string;
  completed: boolean;
}
function addTodo(text: string): Todo {
  return { id: String(Date.now()), text, completed: false };
}
console.log(addTodo("Write code"));

Project Usage

In a real project, this same Todo interface gets reused across the add form, the list-rendering component, and the toggle/delete handlers, so a change to the todo shape (like adding a priority field) is caught everywhere it's used, not just where it's defined.

Example: Project Usage

typescript
interface Todo {
  id: string;
  text: string;
  completed: boolean;
}
function toggleTodo(todos: Todo[], id: string): Todo[] {
  return todos.map((t) => (t.id === id ? { ...t, completed: !t.completed } : t));
}
console.log(toggleTodo([{ id: "1", text: "Task", completed: false }], "1"));

Best Practices

Keep the Todo type in its own small module imported everywhere it's needed, rather than redefining an inline {id, text, completed} object type separately in each component that touches it.

Example: Best Practices

typescript
// todo.ts (its own small module)
export interface Todo {
  id: string;
  text: string;
  completed: boolean;
}
console.log("Todo interface imported everywhere it's needed, defined once");

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.