TypeScript Tutorial: Creating a Simple Web Application for an Online Shop

In today’s fast-paced digital world, online shopping has become the norm. Imagine building your own virtual store, where customers can browse products, add items to a cart, and even simulate the checkout process. This tutorial will guide you through creating a basic online shop application using TypeScript, a powerful superset of JavaScript that adds static typing. We’ll focus on the core functionalities, providing a solid foundation for you to expand upon and customize.

Why TypeScript?

Before diving into the code, let’s explore why TypeScript is an excellent choice for this project. TypeScript offers several benefits:

  • Type Safety: TypeScript allows you to define the types of variables, function parameters, and return values. This helps catch errors early in the development process, reducing debugging time and improving code reliability.
  • Code Readability: With type annotations, your code becomes more self-documenting. It’s easier to understand the purpose of variables and functions.
  • Improved Developer Experience: TypeScript provides excellent tooling support, including autocompletion, refactoring, and error checking in your IDE (Integrated Development Environment).
  • Scalability: As your project grows, TypeScript’s type system helps you manage complexity and maintain code quality.

Setting Up the Development Environment

To get started, you’ll need the following:

  • Node.js and npm (Node Package Manager): These are essential for running JavaScript and managing project dependencies. You can download them from nodejs.org.
  • A Code Editor: Choose your favorite code editor, such as Visual Studio Code, Sublime Text, or Atom.

Once you have these installed, let’s create a new project:

  1. Create a Project Directory: Open your terminal or command prompt and create a new directory for your project. For example: mkdir online-shop
  2. Navigate to the Project Directory: Change your current directory to the project directory: cd online-shop
  3. Initialize npm: Run the following command to initialize an npm project. This will create a package.json file, which stores project metadata and dependencies: npm init -y
  4. Install TypeScript: Install TypeScript as a development dependency: npm install --save-dev typescript
  5. Create a tsconfig.json file: This file configures the TypeScript compiler. Run the following command to generate a basic tsconfig.json file: npx tsc --init

Your project directory should now look something like this:

online-shop/
├── node_modules/
├── package.json
├── package-lock.json
├── tsconfig.json
└──

Project Structure

Let’s define a simple project structure to keep our code organized:

online-shop/
├── src/
│   ├── models/
│   │   └── product.ts
│   ├── components/
│   │   ├── product-list.ts
│   │   └── cart.ts
│   ├── app.ts
│   └── index.html
├── dist/
├── tsconfig.json
├── package.json
└──
  • src/: This directory will contain our TypeScript source code.
  • src/models/: This directory will hold the data models, such as the Product model.
  • src/components/: This directory will contain the UI components, such as product lists and the shopping cart.
  • src/app.ts: This file will contain the main application logic.
  • src/index.html: This file will be the entry point of our web application.
  • dist/: This directory will store the compiled JavaScript files.

Creating the Product Model

Let’s start by creating a Product model to represent the products in our online shop. Create a file named product.ts inside the src/models/ directory:

// src/models/product.ts

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

This code defines an interface called Product with properties for id, name, description, price, and imageUrl. Interfaces in TypeScript define the structure of objects.

Building the Product List Component

Next, let’s create a component to display a list of products. Create a file named product-list.ts inside the src/components/ directory:


// src/components/product-list.ts
import { Product } from '../models/product';

export class ProductList {
  products: Product[];
  element: HTMLElement;

  constructor(products: Product[], element: HTMLElement) {
    this.products = products;
    this.element = element;
  }

  render() {
    this.element.innerHTML = ''; // Clear existing content
    this.products.forEach(product => {
      const productElement = document.createElement('div');
      productElement.classList.add('product');
      productElement.innerHTML = `
        <img src="${product.imageUrl}" alt="${product.name}" />
        <h3>${product.name}</h3>
        <p>${product.description}</p>
        <p>$${product.price.toFixed(2)}</p>
        <button>Add to Cart</button>
      `;
      this.element.appendChild(productElement);
    });
  }
}

This code defines a ProductList class that takes an array of Product objects and an HTML element as input. The render() method iterates through the products and creates HTML elements to display each product’s details. It also includes an “Add to Cart” button.

Creating the Shopping Cart Component

Now, let’s create a basic shopping cart component. Create a file named cart.ts inside the src/components/ directory:


// src/components/cart.ts
import { Product } from '../models/product';

export class Cart {
  items: { product: Product; quantity: number }[] = [];
  element: HTMLElement;

  constructor(element: HTMLElement) {
    this.element = element;
  }

  addItem(product: Product) {
    const existingItem = this.items.find(item => item.product.id === product.id);
    if (existingItem) {
      existingItem.quantity++;
    } else {
      this.items.push({ product, quantity: 1 });
    }
    this.render();
  }

  removeItem(productId: number) {
    this.items = this.items.filter(item => item.product.id !== productId);
    this.render();
  }

  getTotal() {
    return this.items.reduce((total, item) => total + item.product.price * item.quantity, 0);
  }

  render() {
    this.element.innerHTML = '';
    if (this.items.length === 0) {
      this.element.innerHTML = '<p>Your cart is empty.</p>';
      return;
    }

    this.items.forEach(item => {
      const cartItemElement = document.createElement('div');
      cartItemElement.classList.add('cart-item');
      cartItemElement.innerHTML = `
        <span>${item.product.name} - $${item.product.price.toFixed(2)} x ${item.quantity}</span>
        <button data-product-id="${item.product.id}">Remove</button>
      `;
      this.element.appendChild(cartItemElement);
    });

    const totalElement = document.createElement('p');
    totalElement.textContent = `Total: $${this.getTotal().toFixed(2)}`;
    this.element.appendChild(totalElement);

  }
}

The Cart class manages the items in the cart, allowing you to add, remove, and calculate the total price. The render() method displays the cart contents.

Creating the Main Application Logic

Now, let’s put everything together in our main application file, app.ts. Create this file in the src/ directory:


// src/app.ts
import { Product } from './models/product';
import { ProductList } from './components/product-list';
import { Cart } from './components/cart';

// Sample product data
const productsData: Product[] = [
  {
    id: 1,
    name: 'T-Shirt',
    description: 'A comfortable cotton t-shirt.',
    price: 19.99,
    imageUrl: 'https://via.placeholder.com/150/0077CC/FFFFFF?text=T-Shirt'
  },
  {
    id: 2,
    name: 'Jeans',
    description: 'Stylish denim jeans.',
    price: 49.99,
    imageUrl: 'https://via.placeholder.com/150/FF6600/FFFFFF?text=Jeans'
  },
  {
    id: 3,
    name: 'Sneakers',
    description: 'Trendy athletic sneakers.',
    price: 79.99,
    imageUrl: 'https://via.placeholder.com/150/009900/FFFFFF?text=Sneakers'
  },
];

// Get elements from the DOM
const productListElement = document.getElementById('product-list') as HTMLElement;
const cartElement = document.getElementById('cart') as HTMLElement;

// Create instances of the components
const productList = new ProductList(productsData, productListElement);
const cart = new Cart(cartElement);

// Render the product list
productList.render();

// Add event listeners for the "Add to Cart" buttons
productListElement.addEventListener('click', (event: MouseEvent) => {
  const target = event.target as HTMLElement;
  if (target.tagName === 'BUTTON' && target.textContent === 'Add to Cart') {
    const productElement = target.closest('.product') as HTMLElement;
    const productName = productElement.querySelector('h3')!.textContent!;
    const selectedProduct = productsData.find(product => product.name === productName);
    if (selectedProduct) {
      cart.addItem(selectedProduct);
    }
  }
});

This code imports the Product, ProductList, and Cart classes. It also includes sample product data, gets references to HTML elements (product-list and cart), creates instances of the ProductList and Cart components, renders the product list, and adds event listeners to the “Add to Cart” buttons.

Creating the HTML Structure

Now, let’s create the HTML structure for our application. Create a file named index.html in the src/ directory:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Online Shop</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>Welcome to Our Online Shop</h1>
    <div id="product-list"></div>
    <div id="cart">
        <h2>Shopping Cart</h2>
    </div>
    <script src="bundle.js"></script>
</body>
</html>

This HTML file sets up the basic structure of the page, including the title, a container for the product list (product-list), and a container for the shopping cart (cart). It also includes a link to a CSS file (style.css) and a script tag to include the compiled JavaScript file (bundle.js), which we’ll generate in the next step.

Compiling the TypeScript Code

Before we can run our application, we need to compile the TypeScript code into JavaScript. We’ll use the TypeScript compiler for this. First, we need to configure our `tsconfig.json` to specify how the TypeScript compiler should work. Open `tsconfig.json` and ensure it includes the following settings (you may need to modify your existing `tsconfig.json` file):


{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "moduleResolution": "node",
    "sourceMap": true,
    "outDir": "dist",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}

These settings configure the compiler to:

  • target: "es5": Compile to ECMAScript 5 (for broader browser compatibility).
  • module: "esnext": Use ES modules.
  • moduleResolution: "node": Resolve modules using Node.js style.
  • sourceMap: true: Generate source maps for easier debugging.
  • outDir: "dist": Output compiled JavaScript files to the dist directory.
  • esModuleInterop: true: Enables interoperability between CommonJS and ES modules.
  • forceConsistentCasingInFileNames: true: Enforces consistent casing in file names.
  • strict: true: Enables strict type checking.
  • skipLibCheck: true: Skips type checking of declaration files.
  • include: ["src/**/*"]: Include all TypeScript files in the src directory.

Now, run the following command in your terminal to compile the TypeScript code:

npx tsc

This command will compile all TypeScript files in the src directory and output the compiled JavaScript files into the dist directory. You should now see a dist directory containing app.js, product-list.js, cart.js, and product.js, along with corresponding source map files (.map files) for debugging.

Adding Basic Styling (Optional)

To make our online shop look a bit better, let’s add some basic styling using CSS. Create a file named style.css in the src/ directory and add the following CSS rules:


/* src/style.css */

body {
  font-family: sans-serif;
  margin: 20px;
}

h1 {
  text-align: center;
}

#product-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
  margin-bottom: 20px;
}

.product {
  border: 1px solid #ccc;
  padding: 10px;
  text-align: center;
}

.product img {
  max-width: 100%;
  height: auto;
  margin-bottom: 10px;
}

button {
  background-color: #4CAF50;
  color: white;
  padding: 10px 20px;
  border: none;
  cursor: pointer;
  border-radius: 5px;
}

#cart {
  border: 1px solid #ccc;
  padding: 10px;
}

.cart-item {
  margin-bottom: 5px;
}

Running the Application

To run the application, we’ll use a simple web server. A basic way to do this is with the `serve` package, which is a static file server. First, install `serve` globally:

npm install -g serve

Then, navigate to your project directory in your terminal and run the following command to serve the `dist` folder:

serve -d dist

This will start a web server and typically give you a URL like `http://localhost:5000`. Open this URL in your web browser to see your online shop application.

Common Mistakes and How to Fix Them

Here are some common mistakes and how to fix them:

  • Type Errors: TypeScript’s type system will catch type errors during development. Make sure to fix these errors before running your application. Read the error messages carefully, as they often provide helpful hints.
  • Incorrect Paths: Double-check the paths to your modules (e.g., in the import statements) to ensure they are correct.
  • Incorrect HTML Element References: Make sure you have the correct id attributes in your HTML and that you’re referencing them correctly in your TypeScript code. Use the browser’s developer tools (usually accessed by pressing F12) to inspect the HTML and check for any errors.
  • Missing Event Listeners: Ensure that your event listeners (e.g., for the “Add to Cart” buttons) are correctly attached to the appropriate elements.
  • Compiler Errors: If you get errors during compilation (e.g., using npx tsc), carefully review the error messages and the tsconfig.json file for any issues.

Key Takeaways

  • TypeScript provides type safety, code readability, and improved developer experience.
  • We created a basic online shop application with product listings and a shopping cart.
  • We used components to structure the application.
  • We learned how to compile TypeScript code.
  • We covered common mistakes and how to fix them.

FAQ

  1. Can I add more features to this online shop? Yes, you can! This is just a starting point. You can add features such as product filtering, user authentication, a checkout process, and more.
  2. How do I handle more complex data? You can use more advanced data structures, such as arrays and objects, and consider using a state management library for managing application data.
  3. How do I deploy this application? You can deploy your application to a hosting platform like Netlify, Vercel, or GitHub Pages. You’ll need to build your application (using npx tsc) and then deploy the contents of the dist folder.
  4. How can I improve the user interface? You can use a CSS framework like Bootstrap or Tailwind CSS or create custom CSS styles to improve the visual appearance of your application.

This tutorial has provided a foundational understanding of creating a basic online shop application using TypeScript. The skills learned here can be extended to more complex applications. You’ve seen the power of TypeScript in action, providing a robust and scalable foundation for your projects. As you continue to build and experiment, remember to leverage the resources available online, such as the official TypeScript documentation, community forums, and online tutorials, to deepen your knowledge and stay current with the latest best practices. Remember, practice is key. By building more complex features, you’ll gain valuable experience and become more proficient in TypeScript development.