← back to all projects
Inventory Management System (TypeScript)
Part 9 — TypeScript
Why This Project
An inventory system has the kind of data where TypeScript shines: products with
different categories that have different attributes (a shirt has size and color,
a laptop has RAM and storage), stock transactions (restock, sale, adjustment) with
different required fields, and computed values (stock level, total value). You'll
use discriminated unions, generics, and utility types in places where they naturally
solve real problems.
What to Build
A system for a small store to manage their products. Add products with category-specific
attributes, record stock changes (received shipment, sold, damaged/adjusted), view
current inventory levels, and see a dashboard with low-stock alerts and total
inventory value. Full stack: Express + MongoDB backend and React frontend, both
in TypeScript. Shared type definitions between both.
Requirements
- Shared types (used by both frontend and backend):
- Discriminated union for product categories:
type Product = ClothingProduct | ElectronicsProduct | FoodProduct — each has a category field and category-specific attributes
- Discriminated union for stock transactions:
type Transaction = Restock | Sale | Adjustment — each has a type field and different required fields (Restock has supplier, Sale has customer reference, Adjustment has reason)
- Utility types:
Omit<Product, 'id'> for creation payloads, Partial<Product> for updates, Pick for list views vs detail views
- Backend (Express + TypeScript):
- Typed request handlers — type the request body, params, and response for every route
- Mongoose models with TypeScript interfaces
- Validation: use type narrowing to validate incoming data (is this a valid ClothingProduct or an ElectronicsProduct?)
- No
any anywhere — use unknown and narrow
- Frontend (React + TypeScript + Vite):
- Add product form that changes fields based on selected category (narrowing in the UI)
- Inventory table with sorting and filtering
- Stock transaction form (type changes available fields)
- Dashboard: low stock alerts (items below a threshold), total value per category
- All components and hooks have explicit type annotations
- tsconfig strict mode enabled on both sides
Skills Practiced
TypeScript
discriminated unions
type narrowing
generics
utility types
shared types
Mongoose + TypeScript
typed Express handlers
strict mode
Pain Point to Notice
The dynamic form that changes fields based on product category is where TypeScript
earns its keep. Without types, you'd pass the wrong fields and only find out at
runtime. With discriminated unions, the compiler tells you: "A ClothingProduct needs
a size field, you're missing it." The form's onChange handler needs to narrow the
category type before accessing category-specific fields. This is annoying at first
but it prevents an entire class of bugs — sending malformed data to your API.