Lesson 3
Managing Employee Records with Type Safety in TypeScript
Introduction

Welcome! Today, we are going to explore an engaging task that involves managing employee records within a company. Specifically, we will work with nested objects and arrays to add projects and tasks for employees and retrieve those tasks as needed. This exercise will help you understand how to manipulate nested data structures efficiently using TypeScript's type safety features.

Introducing Methods to Implement

Let's start by discussing the methods we will implement in our EmployeeRecords class, utilizing type annotations for method parameters and return types to provide type safety.

  • addProject(employeeId: string, projectName: string): boolean — This method adds a new project to an employee's list of projects. If the project already exists for that employee, the method returns false. Otherwise, it adds the project and returns true.
  • addTask(employeeId: string, projectName: string, task: string): boolean — This method adds a new task to a specified project for an employee. If the project does not exist for that employee, the method returns false. If the task is added successfully, it returns true.
  • getTasks(employeeId: string, projectName: string): string[] | null — This method retrieves all tasks for a specified project of an employee. If the project does not exist for that employee, the method returns null. Otherwise, it returns the list of tasks.
Step 1: Basic Class Structure

Now, let's build our EmployeeRecords class step by step, ensuring we understand each component clearly.

TypeScript
1type ProjectTasks = { [projectName: string]: string[] }; 2type EmployeeRecordsStorage = { [employeeId: string]: ProjectTasks }; 3 4class EmployeeRecords { 5 private records: EmployeeRecordsStorage = {}; 6 7 constructor() {} 8}

In this initial setup, we define the EmployeeRecords class with a records object. The type ProjectTasks represents an object where project names are the keys and arrays of tasks are the values. EmployeeRecordsStorage maps employee IDs to their projects and tasks, ensuring flexible and type-safe management of employee records. Each employee ID key corresponds to an object that holds projects and their associated tasks, using TypeScript index signatures.

Step 2: Implementing 'addProject'
TypeScript
1addProject(employeeId: string, projectName: string): boolean { 2 if (!this.records[employeeId]) { 3 this.records[employeeId] = {}; 4 } 5 if (this.records[employeeId][projectName]) { 6 return false; 7 } else { 8 this.records[employeeId][projectName] = []; 9 return true; 10 } 11}

The addProject method is used to add a new project to an employee's record. It first checks if the employee exists in the records. If not, it initializes the employee's record with an empty object to store projects. The method then checks if the specified project already exists; if it does, it returns false. If not, it creates a new entry for the project with an empty array for tasks and returns true.

Step 3: Implementing 'addTask'
TypeScript
1addTask(employeeId: string, projectName: string, task: string): boolean { 2 if (!this.records[employeeId] || !this.records[employeeId][projectName]) { 3 return false; 4 } 5 this.records[employeeId][projectName].push(task); 6 return true; 7}

The addTask method is designed to add a task to a specific project of an employee. Before adding the task, the method verifies if both the employee and the project exist. If either does not exist, it returns false, indicating the task cannot be added. If both exist, it adds the task to the project's list and returns true, confirming the successful addition of the task.

Step 4: Implementing 'getTasks'
TypeScript
1getTasks(employeeId: string, projectName: string): string[] | null { 2 return this.records[employeeId] ? this.records[employeeId][projectName] || null : null; 3}

The getTasks method is used to retrieve all the tasks for a specified project of an employee. It first checks if the employee exists and then verifies the existence of the specified project. If either the employee or project does not exist, the method returns null. If both exist, it returns the list of tasks associated with the project, allowing easy retrieval of project details.

Final Full Code and Usage

Below is the complete code for the EmployeeRecords class, along with examples demonstrating its usage:

TypeScript
1type ProjectTasks = { [projectName: string]: string[] }; 2type EmployeeRecordsStorage = { [employeeId: string]: ProjectTasks }; 3 4class EmployeeRecords { 5 private records: EmployeeRecordsStorage = {}; 6 7 addProject(employeeId: string, projectName: string): boolean { 8 if (!this.records[employeeId]) { 9 this.records[employeeId] = {}; 10 } 11 if (this.records[employeeId][projectName]) { 12 return false; 13 } else { 14 this.records[employeeId][projectName] = []; 15 return true; 16 } 17 } 18 19 addTask(employeeId: string, projectName: string, task: string): boolean { 20 if (!this.records[employeeId] || !this.records[employeeId][projectName]) { 21 return false; 22 } 23 this.records[employeeId][projectName].push(task); 24 return true; 25 } 26 27 getTasks(employeeId: string, projectName: string): string[] | null { 28 return this.records[employeeId] ? this.records[employeeId][projectName] || null : null; 29 } 30} 31 32// Example usage 33const records = new EmployeeRecords(); 34console.log(records.addProject("E123", "ProjectA")); // Returns true 35console.log(records.addTask("E123", "ProjectA", "Task1")); // Returns true 36console.log(records.getTasks("E123", "ProjectA")); // Returns ["Task1"] 37console.log(records.getTasks("E123", "NonExistentProject")); // Returns null 38console.log(records.addProject("E123", "ProjectA")); // Returns false

This section showcases the EmployeeRecords class with the implemented methods, and how they can be effectively utilized to manage employee records with type safety in TypeScript.

Lesson Summary

In this lesson, we successfully implemented the EmployeeRecords class for managing projects and tasks for employees using nested objects and arrays in TypeScript. We applied TypeScript's type annotations to provide enhanced type safety and code reliability. By taking advantage of TypeScript's features, you can ensure data integrity and catch errors at compile time, improving the robustness and maintainability of your code. Understanding how to work with nested data structures and type safety allows you to efficiently manage complex data hierarchies, elevating your programming skills.

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.