TypeScript Tutorial: Creating a Simple Web-Based Calculator

In today’s digital world, calculators are indispensable. From simple arithmetic to complex scientific calculations, they’re essential tools for everyone. While we have readily available calculators on our phones and computers, creating our own web-based calculator can be a fantastic learning experience. This tutorial will guide you through building a simple, yet functional, calculator using TypeScript, a superset of JavaScript that adds static typing. We’ll cover everything from setting up your development environment to handling user input and displaying results. By the end of this tutorial, you’ll have a working calculator and a solid understanding of how TypeScript can enhance your web development projects.

Why TypeScript for a Calculator?

You might wonder, why TypeScript? Why not just use JavaScript? TypeScript offers several advantages, especially for projects of any size:

  • 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 the likelihood of runtime bugs.
  • Improved Code Readability: Types make your code easier to understand and maintain. They act as documentation, clarifying the expected data types.
  • Enhanced Developer Experience: TypeScript provides features like autocompletion and refactoring, improving developer productivity.
  • Scalability: As your projects grow, TypeScript’s type system helps manage complexity, making it easier to scale your application.

While this calculator is simple, using TypeScript from the start will introduce you to these benefits and make it easier to transition to more complex projects later on.

Setting Up Your Development Environment

Before we start coding, we need to set up our development environment. We’ll need:

  • Node.js and npm (Node Package Manager): Node.js is a JavaScript runtime environment, and npm is a package manager that we’ll use to install TypeScript and other dependencies. You can download them from https://nodejs.org/.
  • A Code Editor: You can use any code editor you prefer, such as Visual Studio Code (VS Code), Sublime Text, or Atom. VS Code is highly recommended due to its excellent TypeScript support.

Once you have Node.js and npm installed, open your terminal or command prompt and create a new project directory:

mkdir typescript-calculator
cd typescript-calculator

Next, initialize a new npm project:

npm init -y

This command creates a package.json file in your project directory. Now, install TypeScript as a development dependency:

npm install typescript --save-dev

This command installs the TypeScript compiler and saves it as a development dependency in your package.json file. After installation, create a tsconfig.json file in your project’s root directory. This file configures the TypeScript compiler. You can generate a basic tsconfig.json file using the following command:

npx tsc --init

This command creates a tsconfig.json file with many commented-out options. You can customize these options to fit your project’s needs. For our calculator, we’ll keep the default settings, but we will make some changes. Open tsconfig.json and modify the following settings:


{
  "compilerOptions": {
    "target": "es5",  // Or "es6", "es2015", etc. - choose a target compatible with your browser
    "module": "commonjs", // Or "esnext", "amd", etc. - module system
    "outDir": "./dist",  // Output directory for compiled JavaScript files
    "strict": true,      // Enable strict type checking
    "esModuleInterop": true, // Enables interoperability between CommonJS and ES Modules
    "skipLibCheck": true,  // Skip type checking of declaration files (.d.ts)
    "forceConsistentCasingInFileNames": true // Enforce consistent casing
  },
  "include": [
    "src/**/*" // Include all TypeScript files in the src directory and its subdirectories
  ],
  "exclude": [
    "node_modules"
  ]
}

These settings configure the compiler to output JavaScript files in the dist directory, enable strict type checking, and include all TypeScript files in the src directory. Now, let’s create our project structure. Create a src directory in your project’s root directory. Inside the src directory, create an index.ts file. This is where we’ll write our TypeScript code for the calculator.

Building the Calculator Logic

Let’s start by defining the basic structure of our calculator. We’ll need:

  • A way to store the current input.
  • A way to store the first number.
  • A way to store the selected operator.
  • A way to display the result.

Open src/index.ts and add the following code:


// Define variables to store calculator state
let currentInput: string = '0';
let firstOperand: number | null = null;
let operator: string | null = null;
let resultDisplayed: boolean = false;

// Get the display element from the DOM
const display = document.getElementById('display') as HTMLInputElement;

// Helper function to update the display
function updateDisplay(): void {
  if (display) {
    display.value = currentInput;
  }
}

In this code:

  • We declare variables to store the current input, the first operand, the selected operator, and a flag to indicate whether the result is currently displayed.
  • We get the display element from the HTML using document.getElementById and cast it to an HTMLInputElement to provide type safety.
  • We create an updateDisplay function to update the calculator’s display with the current input.

Handling Number Input

Next, let’s create a function to handle number input. This function will be called when a number button is clicked.


function handleNumber(number: string): void {
  if (resultDisplayed) {
    currentInput = number;
    resultDisplayed = false;
  } else {
    currentInput = currentInput === '0' ? number : currentInput + number;
  }
  updateDisplay();
}

This function does the following:

  • Checks if the result is currently displayed. If it is, it resets the input with the new number and sets resultDisplayed to false.
  • If the result isn’t displayed, it appends the new number to the current input, ensuring that we don’t have leading zeros.
  • Calls updateDisplay() to update the calculator’s display.

Handling Operator Input

Now, let’s create a function to handle operator input. This function will be called when an operator button (+, -, *, /) is clicked.


function handleOperator(op: string): void {
  if (firstOperand === null) {
    firstOperand = parseFloat(currentInput);
    operator = op;
    resultDisplayed = false;
  } else if (operator !== null) {
    calculate();
    operator = op;
    resultDisplayed = false;
  }
}

This function:

  • If there’s no first operand, it parses the current input as a number and stores it as the first operand, and stores the selected operator.
  • If an operator has already been selected, it calculates the result of the current operation and then updates the operator.

Handling the Equals Button

Let’s create the function to handle the equals button. This function will perform the calculation based on the stored operands and operator.


function calculate(): void {
  if (firstOperand !== null && operator !== null) {
    const secondOperand: number = parseFloat(currentInput);
    let calculationResult: number;

    switch (operator) {
      case '+':
        calculationResult = firstOperand + secondOperand;
        break;
      case '-':
        calculationResult = firstOperand - secondOperand;
        break;
      case '*':
        calculationResult = firstOperand * secondOperand;
        break;
      case '/':
        calculationResult = firstOperand / secondOperand;
        break;
      default:
        return;
    }

    currentInput = String(calculationResult);
    firstOperand = calculationResult;
    operator = null;
    resultDisplayed = true;
    updateDisplay();
  }
}

The calculate function:

  • Parses the current input as the second operand.
  • Uses a switch statement to perform the calculation based on the selected operator.
  • Updates the current input with the result, stores the result as the first operand, resets the operator, sets resultDisplayed to true, and updates the display.

Handling the Clear Button

We need a clear button to reset the calculator to its initial state.


function clearCalculator(): void {
  currentInput = '0';
  firstOperand = null;
  operator = null;
  resultDisplayed = false;
  updateDisplay();
}

The clearCalculator function resets all the calculator variables to their initial values and updates the display.

Handling the Decimal Point

Let’s add a function to handle the decimal point.


function handleDecimal(): void {
  if (!currentInput.includes('.')) {
    currentInput += '.';
    updateDisplay();
  }
}

The handleDecimal function adds a decimal point to the current input if one does not already exist.

Putting it all Together: Event Listeners

Now that we have our functions, let’s add event listeners to the calculator buttons. We’ll use document.getElementById to get the button elements and attach event listeners to handle clicks.


// Number buttons
const numberButtons = document.querySelectorAll('.number');
numberButtons.forEach(button => {
  button.addEventListener('click', () => {
    handleNumber(button.textContent || '');
  });
});

// Operator buttons
const operatorButtons = document.querySelectorAll('.operator');
operatorButtons.forEach(button => {
  button.addEventListener('click', () => {
    handleOperator(button.textContent || '');
  });
});

// Equals button
const equalsButton = document.getElementById('equals');
if (equalsButton) {
  equalsButton.addEventListener('click', calculate);
}

// Clear button
const clearButton = document.getElementById('clear');
if (clearButton) {
  clearButton.addEventListener('click', clearCalculator);
}

// Decimal button
const decimalButton = document.getElementById('decimal');
if (decimalButton) {
  decimalButton.addEventListener('click', handleDecimal);
}

In this code, we:

  • Select all number buttons and attach a click event listener to each one. When a number button is clicked, we call handleNumber with the button’s text content.
  • Select all operator buttons and attach a click event listener. When an operator button is clicked, we call handleOperator with the button’s text content.
  • Attach a click event listener to the equals button, calling the calculate function.
  • Attach a click event listener to the clear button, calling the clearCalculator function.
  • Attach a click event listener to the decimal button, calling the handleDecimal function.

Creating the HTML for Your Calculator

Now, let’s create the HTML structure for our calculator. Create an index.html file in your project’s root directory 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">
</head>
<body>
  <div class="calculator">
    <input type="text" id="display" readonly>
    <div class="buttons">
      <button class="operator">+</button>
      <button class="operator">-</button>
      <button class="operator">*</button>
      <button class="operator">/</button>
      <button class="number">7</button>
      <button class="number">8</button>
      <button class="number">9</button>
      <button class="number">4</button>
      <button class="number">5</button>
      <button class="number">6</button>
      <button class="number">1</button>
      <button class="number">2</button>
      <button class="number">3</button>
      <button class="number">0</button>
      <button id="decimal">.</button>
      <button id="equals">=</button>
      <button id="clear">C</button>
    </div>
  </div>
  <script src="dist/index.js"></script>
</body>
</html>

This HTML structure includes:

  • A display input field where the numbers and results will be shown.
  • A container for the calculator buttons.
  • Buttons for numbers (0-9), operators (+, -, *, /), the decimal point, the equals sign, and the clear button.
  • A link to a style.css file (we’ll create this file later for styling).
  • A script tag that links to the compiled JavaScript file (dist/index.js).

Styling the Calculator with CSS

Let’s add some basic styling to our calculator to make it look presentable. Create a style.css file in your project’s root directory and add the following CSS code:


.calculator {
  width: 300px;
  margin: 50px auto;
  border: 1px solid #ccc;
  border-radius: 5px;
  overflow: hidden;
}

#display {
  width: 100%;
  padding: 10px;
  font-size: 20px;
  text-align: right;
  border: none;
  background-color: #f0f0f0;
}

.buttons {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}

button {
  padding: 15px;
  font-size: 18px;
  border: 1px solid #ccc;
  background-color: #eee;
  cursor: pointer;
}

button:hover {
  background-color: #ddd;
}

#equals {
  background-color: #4CAF50;
  color: white;
}

#clear {
  background-color: #f44336;
  color: white;
}

This CSS code styles the calculator with:

  • A container with a defined width, margin, and border.
  • A display input field with padding, font size, and text alignment.
  • A grid layout for the buttons.
  • Basic styling for the buttons, including hover effects and specific colors for the equals and clear buttons.

Compiling and Running the Calculator

Now that we have our TypeScript code, HTML, and CSS, let’s compile the TypeScript code into JavaScript and run the calculator.

In your terminal, navigate to your project directory and run the following command to compile your TypeScript code:

tsc

This command will compile the index.ts file and generate a index.js file in the dist directory. If you made any changes to tsconfig.json, make sure you rerun this command. Now, open the index.html file in your web browser. You should see the calculator interface. You can now interact with the calculator by clicking the buttons. If everything is set up correctly, you should be able to enter numbers, perform calculations, and see the results displayed in the calculator’s display.

Common Mistakes and How to Fix Them

Here are some common mistakes and how to fix them when building a calculator in TypeScript:

  • Incorrect Type Definitions: One of the most common issues is using incorrect type definitions. For example, if you declare a variable as a number, but later assign a string to it, TypeScript will throw an error. Solution: Double-check your type definitions and make sure they match the expected data types. Use type annotations to specify the types of variables, function parameters, and return values.
  • HTML Element Selection Errors: If you use document.getElementById to get an HTML element, and the element doesn’t exist, it will return null. If you try to access properties of null, you will get an error. Solution: Always check if the HTML element exists before accessing its properties. You can use an if statement to check for null. Also, use the non-null assertion operator (!) when you are sure the element exists to avoid these checks.
  • Incorrect Event Listener Attachments: Make sure your event listeners are correctly attached to the HTML elements. If you misspell the element ID or class name, the event listener won’t work. Solution: Double-check the element IDs and class names in your HTML and your JavaScript code. Use the browser’s developer tools to check for any errors in the console.
  • Operator Precedence: If you don’t handle operator precedence correctly, your calculations might produce incorrect results. For example, multiplication and division should be performed before addition and subtraction. Solution: To handle operator precedence, you can use parentheses to group operations or implement a more advanced parsing algorithm. However, for a simple calculator, you can rely on the order of operations as they are entered.
  • Division by Zero Errors: If you attempt to divide a number by zero, your calculator will throw an error. Solution: Add a check in your code to prevent division by zero. You can display an error message or return a specific value (like Infinity) in such cases.

Key Takeaways

  • TypeScript enhances web development with type safety, improved code readability, and a better developer experience.
  • Setting up a TypeScript project involves installing Node.js, npm, and the TypeScript compiler, and configuring the tsconfig.json file.
  • Building a calculator in TypeScript involves defining variables to store calculator state, creating functions to handle number input, operator input, and the equals button.
  • Event listeners are used to handle button clicks and trigger the appropriate functions.
  • Proper HTML and CSS are necessary to create the calculator’s user interface.
  • Understanding common mistakes and how to fix them is crucial for successful development.

FAQ

Here are some frequently asked questions about building a calculator in TypeScript:

  1. Can I use this calculator on a mobile device? Yes, the calculator is built with HTML, CSS, and JavaScript, so it can be used on any device with a web browser, including mobile devices. You may need to adjust the CSS to improve the responsiveness and user experience on smaller screens.
  2. How can I add more advanced features like memory functions (M+, M-, MR, MC)? You can add memory functions by creating additional variables to store the memory value and adding event listeners for the memory buttons. You would also need to update the calculator logic to handle these functions.
  3. Can I add scientific functions like square root, sine, cosine, etc.? Yes, you can add scientific functions by using the built-in Math object in JavaScript. You would need to add buttons for each function and update the calculator logic to call the appropriate Math methods.
  4. How can I deploy this calculator online? You can deploy the calculator online by uploading the HTML, CSS, and JavaScript files to a web server or using a platform like GitHub Pages or Netlify.

Building a web-based calculator with TypeScript is a rewarding project that allows you to learn and apply fundamental programming concepts. By following this tutorial, you’ve gained practical experience with TypeScript, HTML, CSS, and event handling. Remember to experiment, explore, and expand upon this foundation. Try adding more features, refining the design, or integrating it into a larger web application. The skills and knowledge you’ve gained here will serve you well in your journey as a web developer. With a solid understanding of the fundamentals, and a willingness to explore, you can create anything you set your mind to, one line of code at a time.