Project Overview
In this final project, you'll build a fully typed Task Manager application that demonstrates all the TypeScript concepts you've learned.
- Type-safe data models with interfaces and enums
- Generic utility functions
- Discriminated unions for task states
- Class-based service layer
- Strict null checking and error handling
Type Definitions
Start by defining the type system for the application.
Example
// types.ts
export enum Priority { Low = 'low', Medium = 'medium', High = 'high' }
export enum Status { Todo = 'todo', InProgress = 'in-progress', Done = 'done' }
export interface Task {
readonly id: string;
title: string;
description?: string;
priority: Priority;
status: Status;
createdAt: Date;
completedAt?: Date;
}
export type CreateTaskInput = Omit<Task, 'id' | 'createdAt' | 'completedAt' | 'status'>;
export type UpdateTaskInput = Partial<Pick<Task, 'title' | 'description' | 'priority'>>;
export interface TaskFilter {
status?: Status;
priority?: Priority;
search?: string;
} Task Service
Create a generic, type-safe service class for managing tasks.
Example
// taskService.ts
import { Task, CreateTaskInput, UpdateTaskInput, TaskFilter, Status } from './types';
class TaskService {
private tasks: Map<string, Task> = new Map();
create(input: CreateTaskInput): Task {
const task: Task = {
id: crypto.randomUUID(),
...input,
status: Status.Todo,
createdAt: new Date()
};
this.tasks.set(task.id, task);
return task;
}
update(id: string, input: UpdateTaskInput): Task | null {
const task = this.tasks.get(id);
if (!task) return null;
const updated = { ...task, ...input };
this.tasks.set(id, updated);
return updated;
}
filter(criteria: TaskFilter): Task[] {
return Array.from(this.tasks.values()).filter(task => {
if (criteria.status && task.status !== criteria.status) return false;
if (criteria.priority && task.priority !== criteria.priority) return false;
if (criteria.search && !task.title.includes(criteria.search)) return false;
return true;
});
}
}
export default new TaskService(); 