TypeScript Tutorial: Building a Simple Interactive Web-Based Calculator

In the world of web development, creating interactive user interfaces is crucial. One of the most common and essential components of any website or application is a calculator. This tutorial will guide you through building a simple, yet functional, web-based calculator using TypeScript. We’ll cover everything from setting up your project to handling user input and performing calculations. This project is perfect for beginners and intermediate developers looking to expand their TypeScript skills and build something practical.

Why Build a Calculator with TypeScript?

TypeScript offers several advantages over JavaScript, especially when building complex applications. Here’s why using TypeScript for a calculator is a good idea:

  • Type Safety: TypeScript’s static typing helps catch errors early in the development process, reducing debugging time.
  • Code Maintainability: TypeScript code is easier to understand and maintain, especially as the project grows.
  • Improved Developer Experience: TypeScript provides better autocompletion, refactoring, and other features that enhance the developer experience.
  • Scalability: TypeScript allows you to build more complex applications with greater confidence.

By building a calculator, you’ll gain hands-on experience with these benefits and learn how to apply them to real-world projects.

Setting Up Your Project

Before we start coding, let’s set up our project environment. You’ll need Node.js and npm (Node Package Manager) installed on your system. If you don’t have them, you can download them from the official Node.js website. Open your terminal or command prompt and follow these steps:

  1. Create a Project Directory:
    mkdir typescript-calculator
    cd typescript-calculator
  2. Initialize npm:
    npm init -y
  3. Install TypeScript:
    npm install typescript --save-dev
  4. Create a `tsconfig.json` file:

    This file configures the TypeScript compiler. You can create it by running the following command:

    npx tsc --init
  5. Create Source Files:

    Create a directory called `src` and inside that create a file named `calculator.ts`. This is where we will write our TypeScript code.

    mkdir src
    touch src/calculator.ts
  6. Create an HTML file:

    Create an `index.html` file in the root directory. This will be the entry point for our web application.

    touch index.html

Writing the TypeScript Code

Now, let’s dive into the core of our calculator. Open `src/calculator.ts` in your code editor and let’s start writing some code.

Defining the Calculator Class

We’ll start by defining a `Calculator` class to encapsulate all the calculator’s logic.

class Calculator {
    private currentInput: string = '';
    private previousInput: string = '';
    private operator: string | null = null;

    constructor() {}

    // Method to append digits
    appendNumber(number: string): void {
        this.currentInput += number;
    }

    // Method to choose an operation (+, -, *, /)
    chooseOperation(operator: string): void {
        if (this.currentInput === '') return;
        if (this.previousInput !== '') {
            this.calculate();
        }
        this.operator = operator;
        this.previousInput = this.currentInput;
        this.currentInput = '';
    }

    // Method to calculate the result
    calculate(): void {
        let result: number;
        const prev = parseFloat(this.previousInput);
        const current = parseFloat(this.currentInput);
        if (isNaN(prev) || isNaN(current)) return;

        switch (this.operator) {
            case '+':
                result = prev + current;
                break;
            case '-':
                result = prev - current;
                break;
            case '*':
                result = prev * current;
                break;
            case '/':
                if (current === 0) {
                    alert('Cannot divide by zero');
                    this.currentInput = '';
                    return;
                }
                result = prev / current;
                break;
            default:
                return;
        }

        this.currentInput = result.toString();
        this.operator = null;
        this.previousInput = '';
    }

    // Method to clear the display
    clear(): void {
        this.currentInput = '';
        this.previousInput = '';
        this.operator = null;
    }

    // Method to delete the last input
    delete(): void {
        this.currentInput = this.currentInput.slice(0, -1);
    }

    // Method to get the display value
    getDisplayValue(): string {
        return this.currentInput;
    }
}

Let’s break down this code:

  • `currentInput`: Stores the current number being entered.
  • `previousInput`: Stores the previous number before an operator is selected.
  • `operator`: Stores the selected operator (+, -, *, /).
  • `appendNumber()`: Appends a digit to the `currentInput`.
  • `chooseOperation()`: Sets the operator and moves the `currentInput` to `previousInput`. It also performs a calculation if there is a previous operation.
  • `calculate()`: Performs the calculation based on the `previousInput`, `currentInput`, and `operator`. Handles division by zero.
  • `clear()`: Resets all values to their initial state.
  • `delete()`: Removes the last digit from the current input.
  • `getDisplayValue()`: Returns the current input string to display on the calculator.

Creating the HTML Structure

Now, let’s create the HTML structure for our calculator. Open `index.html` and add the following code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TypeScript Calculator</title>
    <link rel="stylesheet" href="style.css"> <!-- Link to your CSS file -->
</head>
<body>
    <div class="calculator-grid">
        <div class="output">
            <div class="previous-operand"></div>
            <div class="current-operand"></div>
        </div>
        <button class="span-two" data-all-clear>AC</button>
        <button data-delete>DEL</button>
        <button data-operation="/">/</button>
        <button data-number>1</button>
        <button data-number>2</button>
        <button data-number>3</button>
        <button data-operation="*">*</button>
        <button data-number>4</button>
        <button data-number>5</button>
        <button data-number>6</button>
        <button data-operation="+">+</button>
        <button data-number>7</button>
        <button data-number>8</button>
        <button data-number>9</button>
        <button data-operation="-">-</button>
        <button data-number>.</button>
        <button data-number>0</button>
        <button data-equals class="span-two">=</button>
    </div>
    <script src="calculator.js"></script> <!-- Link to your compiled JavaScript file -->
</body>
</html>

This HTML structure defines the layout of our calculator. We have:

  • An `output` div to display the previous and current operands.
  • Buttons for numbers (0-9).
  • Buttons for operations (+, -, *, /).
  • Buttons for clear (AC), delete (DEL), and equals (=).

Styling with CSS

To make our calculator visually appealing, let’s add some CSS. Create a file named `style.css` in your project’s root directory and add the following styles:

:root {
    --main-bg-color: #f0f0f0;
    --button-bg-color: #e0e0e0;
    --operator-bg-color: #c0c0c0;
    --text-color: #333;
    --font-size: 1.5rem;
}

body {
    font-family: Arial, sans-serif;
    background-color: var(--main-bg-color);
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    margin: 0;
}

.calculator-grid {
    display: grid;
    grid-template-columns: repeat(4, 6rem);
    grid-template-rows: minmax(7rem, auto) repeat(5, 6rem);
    background-color: #fff;
    border-radius: 1rem;
    padding: 1rem;
    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.2);
}

.calculator-grid > button {
    cursor: pointer;
    font-size: var(--font-size);
    border: 1px solid #ccc;
    outline: none;
    background-color: var(--button-bg-color);
    border-radius: 0.5rem;
}

.calculator-grid > button:hover {
    background-color: #d0d0d0;
}

.span-two {
    grid-column: span 2;
}

.output {
    grid-column: 1 / -1;
    background-color: var(--main-bg-color);
    display: flex;
    align-items: flex-end;
    justify-content: space-around;
    flex-direction: column;
    padding: 0.75rem;
    word-wrap: break-word;
    word-break: break-all;
    border-radius: 0.5rem;
}

.output .previous-operand {
    color: rgba(0, 0, 0, 0.75);
    font-size: 1.5rem;
}

.output .current-operand {
    color: var(--text-color);
    font-size: 2.5rem;
}

[data-operation] {
    background-color: var(--operator-bg-color);
}

[data-all-clear], [data-delete] {
    background-color: var(--operator-bg-color);
}

This CSS provides a basic style for the calculator, including the layout, button styles, and output display. Feel free to customize the colors and styles to your liking.

Implementing Event Listeners and Logic

Now, let’s connect the HTML elements with our TypeScript code. We’ll add event listeners to the buttons to handle user interactions. Add the following code to `src/calculator.ts`:


// Get the calculator from the DOM
const numberButtons = document.querySelectorAll('[data-number]');
const operationButtons = document.querySelectorAll('[data-operation]');
const equalsButton = document.querySelector('[data-equals]');
const deleteButton = document.querySelector('[data-delete]');
const allClearButton = document.querySelector('[data-all-clear]');
const previousOperandTextElement = document.querySelector('.previous-operand') as HTMLElement;
const currentOperandTextElement = document.querySelector('.current-operand') as HTMLElement;

// Initialize the calculator
const calculator = new Calculator();

// Event Listeners
numberButtons.forEach(button => {
    button.addEventListener('click', () => {
        calculator.appendNumber(button.textContent!);
        updateDisplay();
    });
});

operationButtons.forEach(button => {
    button.addEventListener('click', () => {
        calculator.chooseOperation(button.textContent!);
        updateDisplay();
    });
});

equalsButton?.addEventListener('click', () => {
    calculator.calculate();
    updateDisplay();
});

deleteButton?.addEventListener('click', () => {
    calculator.delete();
    updateDisplay();
});

allClearButton?.addEventListener('click', () => {
    calculator.clear();
    updateDisplay();
});

function updateDisplay() {
    currentOperandTextElement.textContent = calculator.getDisplayValue();
    if (calculator.operator != null) {
        previousOperandTextElement.textContent = `${calculator.previousInput} ${calculator.operator}`;
    } else {
        previousOperandTextElement.textContent = '';
    }
}

In this code:

  • We select all the necessary elements from the HTML using `document.querySelector` and `document.querySelectorAll`. The `as HTMLElement` is used to tell TypeScript what type of HTML element we are working with.
  • We initialize an instance of the `Calculator` class.
  • We add event listeners to each button. When a button is clicked, the corresponding method of the `Calculator` class is called, and the display is updated.
  • `updateDisplay()` updates the content of the display elements based on the calculator’s state.

Compiling and Running the Code

Now that we’ve written our code, let’s compile it and run it in the browser. Open your terminal and run the following command to compile the TypeScript code:

tsc

This command will compile your `calculator.ts` file and create a `calculator.js` file in the same directory. Make sure you’ve linked the compiled JavaScript file in your `index.html` file (as we did in the HTML section earlier).

To run the calculator, open `index.html` in your web browser. You should see the calculator interface. Click the buttons to enter numbers and perform calculations. If everything is set up correctly, the calculator should work as expected. If you encounter any errors, check your browser’s developer console for error messages and review the code for any typos or mistakes.

Common Mistakes and How to Fix Them

Here are some common mistakes and how to fix them:

  • Incorrect File Paths: Ensure that the file paths in your `index.html` file (e.g., for the CSS and JavaScript files) are correct.
  • Typos: TypeScript is case-sensitive. Double-check for any typos in your code, especially variable names and method calls.
  • Missing Event Listeners: Make sure you have added event listeners to all the buttons.
  • Incorrect Logic: Carefully review the logic of your `calculate()` method and other methods to ensure they are performing the correct operations. Use console.log statements to debug.
  • Division by Zero: Implement error handling to prevent division by zero, as shown in the example code.
  • Type Errors: Pay attention to TypeScript’s type errors. These errors can help you identify potential issues before runtime.

Key Takeaways

  • TypeScript Fundamentals: You’ve learned how to use classes, methods, and variables in TypeScript.
  • DOM Manipulation: You’ve learned how to select and manipulate HTML elements using JavaScript.
  • Event Handling: You’ve learned how to handle user interactions using event listeners.
  • Project Structure: You’ve learned how to structure a simple web application project.
  • Debugging: You’ve learned how to identify and fix common errors in your code.

FAQ

  1. Can I add more complex features to the calculator?

    Yes! You can extend the calculator to include features like parentheses, memory functions, trigonometric functions, and more. This tutorial provides a solid foundation for building upon.

  2. How can I deploy this calculator online?

    You can deploy your calculator online using services like Netlify, Vercel, or GitHub Pages. These services allow you to host your static website for free or at a low cost.

  3. How can I improve the user interface?

    You can improve the user interface by adding more CSS styling, using a CSS framework like Bootstrap or Tailwind CSS, or by using a JavaScript UI library like React or Vue.js.

  4. Why use a class instead of just functions?

    Using a class helps encapsulate the calculator’s state and behavior, making the code more organized, maintainable, and scalable. It allows you to create multiple instances of the calculator if needed, and also makes it easier to add new features or modify existing ones without affecting other parts of the code.

Building a web-based calculator with TypeScript is a rewarding project that combines fundamental programming concepts with practical application. By following this tutorial, you’ve not only created a functional calculator but also gained valuable experience with TypeScript, HTML, and CSS. The skills you’ve acquired can be applied to a wide range of web development projects. Remember that practice is key, so don’t hesitate to experiment with the code, add new features, and explore other possibilities. Keep learning, keep coding, and keep building!