🔥 0
0
Lesson 4 of 10 20 min +100 XP

Arrays & Objects

E-commerce apps are full of lists: product catalogs, cart items, order history. Let's master typing them.

Arrays of Objects

Combine what we learned about arrays and interfaces:

interface Product {
  id: string;
  name: string;
  price: number;
  inStock: boolean;
}

// Array of products
const catalog: Product[] = [
  { id: "SKU-001", name: "Wireless Mouse", price: 29.99, inStock: true },
  { id: "SKU-002", name: "USB-C Hub", price: 49.99, inStock: true },
  { id: "SKU-003", name: "Webcam HD", price: 79.99, inStock: false }
];

// TypeScript knows each item is a Product
catalog.forEach(product => {
  console.log(`${product.name}: ${product.price}`);
  // Autocomplete works! product.na... suggests 'name'
});

Array Methods with Types

TypeScript tracks types through array operations:

const products: Product[] = [
  { id: "SKU-001", name: "Keyboard", price: 99.99, inStock: true },
  { id: "SKU-002", name: "Mouse", price: 29.99, inStock: true },
  { id: "SKU-003", name: "Monitor", price: 299.99, inStock: false }
];

// filter() returns Product[]
const available = products.filter(p => p.inStock);
// TypeScript knows: available is Product[]

// map() returns the mapped type
const names = products.map(p => p.name);
// TypeScript knows: names is string[]

const prices = products.map(p => p.price);
// TypeScript knows: prices is number[]

// find() returns Product | undefined (might not find it!)
const keyboard = products.find(p => p.name === "Keyboard");
// TypeScript knows: keyboard is Product | undefined

if (keyboard) {
  console.log(keyboard.price);  // Safe to access after check
}

// reduce() - type the accumulator
const totalValue = products.reduce(
  (sum, product) => sum + product.price,
  0  // Initial value tells TS the return type is number
);

---

Index Signatures

Sometimes you don't know property names ahead of time - like a product inventory by SKU:

// Stock levels by product ID
interface Inventory {
  [productId: string]: number;  // Any string key -> number value
}

const stock: Inventory = {
  "SKU-001": 50,
  "SKU-002": 125,
  "SKU-003": 0,
  "SKU-004": 30
};

// Access dynamically
const sku = "SKU-002";
console.log(`Stock for ${sku}: ${stock[sku]}`);  // 125

// Add new entries
stock["SKU-005"] = 75;

Combining with Known Properties

interface ProductMap {
  lastUpdated: Date;                    // Known property
  [productId: string]: Product | Date;  // Dynamic properties
}

Better approach - use a nested structure:

interface Catalog {
  lastUpdated: Date;
  products: {
    [productId: string]: Product;
  };
}

const catalog: Catalog = {
  lastUpdated: new Date(),
  products: {
    "SKU-001": { id: "SKU-001", name: "Mouse", price: 29.99, inStock: true },
    "SKU-002": { id: "SKU-002", name: "Keyboard", price: 99.99, inStock: true }
  }
};

---

The Record Utility Type

A cleaner way to type objects with dynamic keys:

// Record<KeyType, ValueType>
type Inventory = Record<string, number>;

const stock: Inventory = {
  "SKU-001": 50,
  "SKU-002": 125
};

// Prices by product ID
type PriceList = Record<string, number>;

const prices: PriceList = {
  "SKU-001": 29.99,
  "SKU-002": 49.99
};

// Product lookup by ID
type ProductLookup = Record<string, Product>;

const products: ProductLookup = {
  "SKU-001": { id: "SKU-001", name: "Mouse", price: 29.99, inStock: true }
};

---

Typing the Shopping Cart

Let's build a complete cart type:

interface Product {
  id: string;
  name: string;
  price: number;
  imageUrl?: string;
}

interface CartItem {
  product: Product;
  quantity: number;
}

interface Cart {
  items: CartItem[];
  couponCode: string | null;
}

// Example cart
const cart: Cart = {
  items: [
    {
      product: { id: "SKU-001", name: "Wireless Earbuds", price: 79.99 },
      quantity: 1
    },
    {
      product: { id: "SKU-002", name: "Phone Case", price: 19.99 },
      quantity: 2
    }
  ],
  couponCode: "SAVE10"
};

// Calculate totals (TypeScript validates everything)
const subtotal = cart.items.reduce(
  (sum, item) => sum + (item.product.price * item.quantity),
  0
);

console.log(`Subtotal: ${subtotal.toFixed(2)}`);  // $119.97

---

Readonly Arrays

Prevent accidental mutations:

// Immutable product list
const featuredProducts: readonly Product[] = [
  { id: "SKU-001", name: "Best Seller", price: 99.99, inStock: true }
];

featuredProducts.push({ /* ... */ });  // ❌ Error: Property 'push' does not exist on type 'readonly Product[]'

// Alternative syntax
const categories: ReadonlyArray<string> = ["Electronics", "Clothing", "Home"];
categories[0] = "Tech";  // ❌ Error: Index signature in type 'readonly string[]' only permits reading

---

Type Challenges

Challenge 1: Type the Order History

Create types for an order with multiple items:

// Your types here

const order = {
  orderId: "ORD-12345",
  date: new Date("2024-01-15"),
  items: [
    { productName: "Laptop", price: 999, quantity: 1 },
    { productName: "Mouse", price: 29, quantity: 2 }
  ],
  total: 1057,
  status: "delivered"
};
Solution
interface OrderItem {
  productName: string;
  price: number;
  quantity: number;
}

interface Order {
  orderId: string;
  date: Date;
  items: OrderItem[];
  total: number;
  status: string;  // We'll improve this with union types!
}

const order: Order = {
  orderId: "ORD-12345",
  date: new Date("2024-01-15"),
  items: [
    { productName: "Laptop", price: 999, quantity: 1 },
    { productName: "Mouse", price: 29, quantity: 2 }
  ],
  total: 1057,
  status: "delivered"
};

Challenge 2: Create a Category Map

Type an object where keys are category names and values are arrays of products:

// Your type here

const categoryProducts = {
  electronics: [
    { id: "1", name: "Phone", price: 699, inStock: true }
  ],
  clothing: [
    { id: "2", name: "T-Shirt", price: 25, inStock: true },
    { id: "3", name: "Jeans", price: 59, inStock: false }
  ]
};
Solution
type CategoryProducts = Record<string, Product[]>;

// Or with an interface:
interface CategoryProducts {
  [category: string]: Product[];
}

const categoryProducts: CategoryProducts = {
  electronics: [
    { id: "1", name: "Phone", price: 699, inStock: true }
  ],
  clothing: [
    { id: "2", name: "T-Shirt", price: 25, inStock: true },
    { id: "3", name: "Jeans", price: 59, inStock: false }
  ]
};

---

Key Takeaways

ConceptSyntaxUse Case
Array of objectsProduct[]Product catalog
Index signature[key: string]: TypeDynamic keys
RecordRecordCleaner dynamic objects
Readonly arrayreadonly T[]Immutable lists

Next up: Typed Functions - adding types to addToCart(), calculateTotal(), and more!

Interfaces: Defining Shapes