liveBook · Manning

Data model

As explained in Essential TypeScript 5, Third Edition

A data model is a structured representation of an application's data and the operations that can be performed on it.

A data model gives an application a consistent way to represent its data and perform operations on it. In a to-do application, a TodoItem holds an ID, a task, and a completion flag; a TodoCollection holds items and provides methods to add, find, and mark them complete. section 2.2.5

Listing 2.12 The contents of the todoCollection.ts file in the src folder
import { TodoItem } from "./todoItem";

export class TodoCollection {
   private nextId: number = 1;
   
   constructor(public userName: string, 
               public todoItems: TodoItem[] = []) {
       // no statements required
   }

   addTodo(task: string): number {
       while (this.getTodoById(this.nextId)) {
           this.nextId++;
       }        
       this.todoItems.push(new TodoItem(this.nextId, task));
       return this.nextId;
   }

   getTodoById(id: number) : TodoItem {
       return this.todoItems.find(item => item.id === id);
   }

   markComplete(id: number, complete: boolean) {
       const todoItem = this.getTodoById(id);
       if (todoItem) {
           todoItem.complete = complete;
       }
   }
}

The todoCollection.ts listing shows how a collection stores typed items and puts operations alongside the data they change. section 2.2.5

Choose a type alias when the model needs a shape for related properties, and a class when it also needs behavior. A product is described by fields such as ID, name, and price. An order line pairs a product with a quantity and calculates its total; an order stores lines in a map keyed by product ID. section 16.5

Listing 16.15 The contents of the entities.ts file in the src/data folder
export type Product = {
   id: number, 
   name: string,
   description: string,
   category: string, 
   price: number 
};

export class OrderLine {
   constructor(public product: Product, public quantity: number) {
       // no statements required
   }

   get total(): number {
       return this.product.price * this.quantity;
   }
}

export class Order {
   private lines = new Map<number, OrderLine>();

   constructor(initialLines?: OrderLine[]) {
       if (initialLines) {
           initialLines.forEach(ol => this.lines.set(ol.product.id, ol));
       }
   }

   public addProduct(prod: Product, quantity: number) {
       if (this.lines.has(prod.id)) {
           if (quantity === 0) {
               this.removeProduct(prod.id);
           } else {
               this.lines.get(prod.id)!.quantity += quantity;
           }
       } else {
           this.lines.set(prod.id, new OrderLine(prod, quantity));
       }
   }

   public removeProduct(id: number) {
       this.lines.delete(id);
   }

   get orderLines(): OrderLine[] {
       return [...this.lines.values()];
   }

   get productCount(): number {
       return [...this.lines.values()]
           .reduce((total, ol) => total += ol.quantity, 0);
   }

   get total(): number {
       return [...this.lines.values()]
           .reduce((total, ol) => total += ol.total, 0);
   }
}

The entities.ts listing puts the Product type alias beside the OrderLine and Order classes, making the distinction between data shape and behavior concrete. section 16.5

Catch a mismatched operation

addTodo expects a task string. Passing it a TodoItem instead produces a compiler error: the argument is not assignable to string. The annotations on the model make that mistake visible before execution. section 2.2.5

The same Product, OrderLine, and Order definitions can be placed in an Angular application's src/app/data/entities.ts without changing the entity code: Angular uses regular TypeScript classes for its data model. section 18.3

How Essential TypeScript 5, Third Edition covers it

How deep it goes
Builds typed entities and collection methods, then demonstrates a compiler error; defers fuller explanation of classes and JavaScript types.
Examples
Uses a to-do list with add and completion operations and a product order with quantities and calculated line totals.
What it assumes
Assumes some programming familiarity with classes while introducing TypeScript annotations; defers detailed JavaScript type behavior.
What you can do after
Build it from scratch as typed TypeScript entities with operations, and recognize a mismatched method argument through compiler feedback.
How it connects
Connects classes, type aliases, static typing, collections, and HTTP-backed product orders.

Also explained in

Compare how 23 books explain data model →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Essential TypeScript 5, Third Edition. The section links point to the parts of the book it draws on.