TypeScript: Building a Simple Web-Based Number Guessing Game

Ever found yourself staring at a blank screen, yearning to build something interactive but feeling overwhelmed by the complexities of modern web development? You’re not alone. Many developers, especially those just starting out, often struggle to translate their ideas into functional code. This tutorial aims to bridge that gap by guiding you through the creation of a fun and engaging number guessing game using TypeScript, a powerful and increasingly popular language.

Why TypeScript?

TypeScript, a superset of JavaScript, brings static typing to the dynamic world of web development. This means you can catch errors early, improve code readability, and enjoy better tooling support. For beginners, this translates to a smoother learning curve and a more robust coding experience. While JavaScript allows for a lot of flexibility, TypeScript provides structure, making your code easier to manage as your projects grow.

What We’ll Build

We’ll create a simple number guessing game where the computer randomly selects a number, and the player has to guess it. The game will provide feedback on whether the guess is too high or too low, and it will track the number of attempts. This project is ideal for learning fundamental TypeScript concepts and applying them in a practical, interactive context.

Prerequisites

  • Basic understanding of HTML, CSS, and JavaScript.
  • Node.js and npm (Node Package Manager) installed.
  • A code editor (like VS Code) with TypeScript support.

Setting Up the Project

Let’s start by setting up our project. Open your terminal or command prompt and navigate to the directory where you want to create your project. Then, execute the following commands:

mkdir number-guessing-game
cd number-guessing-game
npm init -y
npm install typescript --save-dev

This will create a new directory, initialize a Node.js project, and install TypeScript as a development dependency. Next, create a tsconfig.json file in the root of your project. This file configures the TypeScript compiler. You can generate a basic one using the command: npx tsc --init. You can customize the tsconfig.json file to suit your needs, but a basic configuration is sufficient for this project. Here’s a sample configuration:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "outDir": "./dist",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
  }
}

Now, create a file named index.ts in the root directory. This is where we’ll write our TypeScript code.

Writing the TypeScript Code

Let’s dive into the core logic of our game. We’ll start by defining the necessary variables and functions.


// Generate a random number between 1 and 100
const randomNumber = Math.floor(Math.random() * 100) + 1;
let attempts = 0;

// Function to handle the player's guess
function guessNumber() {
    const guess = parseInt(prompt("Guess the number (1-100):") || "0");
    attempts++;

    if (isNaN(guess) || guess  100) {
        alert("Please enter a valid number between 1 and 100.");
        return;
    }

    if (guess === randomNumber) {
        alert(`Congratulations! You guessed the number in ${attempts} attempts.`);
        resetGame();
    } else if (guess < randomNumber) {
        alert("Too low! Try again.");
    } else {
        alert("Too high! Try again.");
    }
}

// Function to reset the game
function resetGame() {
    attempts = 0;
    const newRandomNumber = Math.floor(Math.random() * 100) + 1;
    randomNumber = newRandomNumber;  // Update randomNumber
    // Optionally, you can add a confirmation message here, like "New game started!"
}

// Start the game
guessNumber();

Let’s break down this code:

  • randomNumber: Stores the randomly generated number that the player needs to guess.
  • attempts: Keeps track of the number of guesses the player has made.
  • guessNumber(): This is the main function that handles the game logic.
  • prompt(): This built-in JavaScript function displays a dialog box asking the user to input a value. We use it to get the player’s guess.
  • parseInt(): Converts the player’s input (which is initially a string) into an integer.
  • isNaN(): Checks if the input is a valid number.
  • alert(): Displays a message to the player, providing feedback on their guess.
  • resetGame(): Resets the game to start a new round.

Compiling and Running the Code

To compile your TypeScript code, open your terminal and run the following command from your project’s root directory:

tsc

This command will compile your index.ts file and generate a index.js file in the dist directory (as specified in your tsconfig.json). To run the game, you’ll need to use a browser environment. Create an index.html file in the 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>Number Guessing Game</title>
</head>
<body>
    <script src="dist/index.js"></script>
</body>
</html>

Open index.html in your browser. You should see a prompt asking you to guess the number. Enter your guesses and see if you can win!

Adding User Interface (UI) Elements

While the game works, it’s currently limited by its reliance on the prompt() and alert() functions, which can be clunky. Let’s enhance the user experience by creating a simple UI using HTML elements. Modify your index.html file as follows:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Number Guessing Game</title>
</head>
<body>
    <h1>Number Guessing Game</h1>
    <p>Guess a number between 1 and 100:</p>
    <input type="number" id="guessInput">
    <button id="guessButton">Guess</button>
    <p id="feedback"></p>
    <p id="attempts">Attempts: 0</p>
    <script src="dist/index.js"></script>
</body>
</html>

Now, let’s update our index.ts file to interact with these UI elements:


// Generate a random number between 1 and 100
const randomNumber = Math.floor(Math.random() * 100) + 1;
let attempts = 0;

// Get references to the HTML elements
const guessInput = document.getElementById('guessInput') as HTMLInputElement;
const guessButton = document.getElementById('guessButton') as HTMLButtonElement;
const feedback = document.getElementById('feedback') as HTMLParagraphElement;
const attemptsDisplay = document.getElementById('attempts') as HTMLParagraphElement;

// Function to handle the player's guess
function checkGuess() {
    const guess = parseInt(guessInput.value);
    attempts++;

    if (isNaN(guess) || guess  100) {
        feedback.textContent = "Please enter a valid number between 1 and 100.";
        return;
    }

    if (guess === randomNumber) {
        feedback.textContent = `Congratulations! You guessed the number in ${attempts} attempts.`;
        resetGame();
    } else if (guess < randomNumber) {
        feedback.textContent = "Too low! Try again.";
    } else {
        feedback.textContent = "Too high! Try again.";
    }

    attemptsDisplay.textContent = `Attempts: ${attempts}`;
}

// Function to reset the game
function resetGame() {
    attempts = 0;
    const newRandomNumber = Math.floor(Math.random() * 100) + 1;
    randomNumber = newRandomNumber;
    feedback.textContent = ""; // Clear feedback
    attemptsDisplay.textContent = "Attempts: 0";
    guessInput.value = ""; // Clear input field
}

// Add an event listener to the guess button
guessButton.addEventListener('click', checkGuess);

Key changes include:

  • We get references to the HTML elements using document.getElementById().
  • We use type assertions (e.g., as HTMLInputElement) to tell TypeScript what type of HTML element each variable represents. This helps with type checking and code completion.
  • We replace the prompt() and alert() functions with the use of the UI elements.
  • We add an event listener to the “Guess” button so that when the user clicks it, it calls the checkGuess() function.

Recompile your TypeScript code with tsc, refresh your browser, and play the game using the input field and button!

Adding CSS for Styling

To make the game more visually appealing, let’s add some basic CSS styling. Create a new file named style.css in the root directory and add the following code:


body {
    font-family: sans-serif;
    text-align: center;
}

h1 {
    color: #333;
}

input[type="number"] {
    padding: 5px;
    font-size: 16px;
}

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

button:hover {
    background-color: #3e8e41;
}

#feedback {
    margin-top: 10px;
    font-weight: bold;
}

Then, link this stylesheet to your index.html file within the <head> section:


<link rel="stylesheet" href="style.css">

Refresh your browser, and you should see the game with the new styles.

Error Handling and Input Validation

We’ve already implemented basic input validation to ensure the player enters a number between 1 and 100. However, consider what happens if the user enters a non-numeric value or leaves the input field empty. We use isNaN() to check if the input is a valid number. We also use parseInt() to convert the input to an integer. However, a more robust solution might include:

  • Preventing the user from entering non-numeric characters in the input field.
  • Providing clearer error messages to guide the user.

Here’s an improved version of the checkGuess() function:


function checkGuess() {
    const inputValue = guessInput.value;

    if (!inputValue) {
        feedback.textContent = "Please enter a number.";
        return;
    }

    const guess = parseInt(inputValue);

    if (isNaN(guess)) {
        feedback.textContent = "Please enter a valid number.";
        return;
    }

    if (guess  100) {
        feedback.textContent = "Please enter a number between 1 and 100.";
        return;
    }

    attempts++;

    if (guess === randomNumber) {
        feedback.textContent = `Congratulations! You guessed the number in ${attempts} attempts.`;
        resetGame();
    } else if (guess < randomNumber) {
        feedback.textContent = "Too low! Try again.";
    } else {
        feedback.textContent = "Too high! Try again.";
    }

    attemptsDisplay.textContent = `Attempts: ${attempts}`;
}

This improved version checks for empty input and provides more specific error messages.

Refactoring and Code Organization

As your projects grow in complexity, it’s crucial to organize your code effectively. For a simple game like this, we can keep the code in a single file. However, for larger projects, you might consider:

  • Breaking your code into separate modules (files) based on functionality. For example, you could have one module for game logic, one for UI handling, and another for data storage.
  • Using classes to represent game objects and their behavior.
  • Using interfaces to define the structure of your data.

Common Mistakes and How to Fix Them

Here are some common mistakes beginners make when working with TypeScript, along with how to avoid them:

  • Ignoring Type Errors: TypeScript’s main benefit is its type checking. Don’t ignore the errors the compiler throws! They are there to help you catch bugs early. Read the error messages carefully and understand what they are telling you.
  • Incorrect Type Annotations: Make sure your type annotations (e.g., let x: number;) accurately reflect the data you are working with. Incorrect annotations can lead to unexpected behavior.
  • Forgetting to Compile: Always remember to compile your TypeScript code (using tsc) before running it. If you make changes to your .ts files but don’t recompile, your changes won’t be reflected in the .js files that your browser uses.
  • Not Using Type Assertions Correctly: Type assertions (e.g., as HTMLInputElement) can be useful, but use them sparingly. Overusing them can defeat the purpose of type safety. Use them when you are certain about the type of a variable, but the compiler cannot infer it.
  • Not Understanding Scope: Make sure you understand how variables and functions are scoped (i.e., where they are accessible from) in your code. This can prevent unexpected behavior and bugs.

Key Takeaways

  • TypeScript Fundamentals: You’ve learned how to define variables, functions, and use basic types in TypeScript.
  • DOM Manipulation: You’ve seen how to interact with HTML elements using JavaScript and TypeScript.
  • Event Handling: You’ve learned how to handle user events (like button clicks) to make your game interactive.
  • Project Setup: You’ve learned how to set up a basic TypeScript project and compile your code.

FAQ

Here are some frequently asked questions about this project:

  1. Why use TypeScript instead of JavaScript? TypeScript provides static typing, which helps catch errors early, improves code readability, and enhances developer productivity.
  2. How do I debug TypeScript code? You can debug TypeScript code using your browser’s developer tools. The compiled JavaScript code will be available for debugging.
  3. Can I add more features to the game? Absolutely! You could add features such as difficulty levels, a score board, or sound effects.
  4. What are some other TypeScript projects I could try? You could try building a simple to-do list application, a calculator, or a currency converter.
  5. How do I deploy this game? You can deploy your game by uploading the HTML, CSS, and JavaScript files to a web server. Services like Netlify or GitHub Pages are great for this.

This project offers a solid foundation for understanding TypeScript and building interactive web applications. By understanding the fundamentals and practicing, you can take your skills to the next level. Remember to experiment, try new things, and most importantly, have fun while coding! The more you build, the more confident and skilled you will become. Keep learning, keep coding, and keep exploring the amazing possibilities that TypeScript offers.

” ,
“aigenerated_tags”: “TypeScript, Web Development, Game Development, Beginner Tutorial, JavaScript, HTML, CSS