TypeScript Tutorial: Building a Simple Web-Based Weather App

In today’s interconnected world, weather information is more accessible and crucial than ever. From planning your day to understanding global climate patterns, knowing the weather is essential. This tutorial will guide you through building a simple, yet functional, web-based weather application using TypeScript. We’ll cover everything from setting up your development environment to making API calls and displaying dynamic data, all while adhering to best practices and providing clear explanations for developers of all skill levels.

Why Build a Weather App?

Creating a weather application is a fantastic project for learning TypeScript and web development. It allows you to:

  • Apply fundamental TypeScript concepts like types, interfaces, and classes.
  • Work with asynchronous operations using `async/await` and Promises.
  • Interact with external APIs to fetch real-time data.
  • Build a user interface (UI) and handle user interactions.
  • Gain practical experience in structuring and organizing a web application.

By the end of this tutorial, you’ll have a fully functional weather app that you can customize and expand upon. This project provides a solid foundation for more complex web applications and demonstrates how TypeScript can enhance code quality and maintainability.

Prerequisites

Before we begin, make sure you have the following installed and set up:

  • Node.js and npm (or yarn): These are essential for managing project dependencies and running the development server. You can download them from nodejs.org.
  • A code editor: Visual Studio Code (VS Code) is highly recommended due to its excellent TypeScript support. You can download it from code.visualstudio.com.
  • Basic understanding of HTML, CSS, and JavaScript: While this tutorial focuses on TypeScript, a basic grasp of these technologies is necessary for building the user interface.

Setting Up the Project

Let’s start by setting up our project. Open your terminal or command prompt and follow these steps:

  1. Create a new project directory:
    mkdir weather-app
     cd weather-app
  2. Initialize a new npm project:
    npm init -y

    This command creates a `package.json` file, which manages your project’s dependencies.

  3. Install TypeScript and related dependencies:
    npm install typescript @types/node --save-dev
    • `typescript`: The TypeScript compiler.
    • `@types/node`: Type definitions for Node.js, allowing TypeScript to understand Node.js modules.
  4. Create a `tsconfig.json` file:
    npx tsc --init

    This command creates a `tsconfig.json` file, which configures the TypeScript compiler. You can customize this file to control how TypeScript compiles your code. We’ll modify it slightly later.

  5. Create project files:

    Create the following files in your project directory:

    • `index.html`: The HTML file for your app.
    • `src/index.ts`: The main TypeScript file.
    • `src/style.css`: The CSS file for styling.

Configuring TypeScript (tsconfig.json)

Open `tsconfig.json` in your code editor. We’ll make a few changes to optimize the compilation process:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "outDir": "./dist",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}

Here’s a breakdown of the key options:

  • target: "es5": Specifies the JavaScript version to compile to.
  • module: "commonjs": Specifies the module system.
  • outDir: "./dist": Sets the output directory for compiled JavaScript files.
  • 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/**/*"]: Specifies the files to include in the compilation.

Writing the HTML (index.html)

Let’s create the basic HTML structure for our weather app. 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>Weather App</title>
    <link rel="stylesheet" href="src/style.css">
</head>
<body>
    <div class="container">
        <h1>Weather App</h1>
        <div class="search">
            <input type="text" id="cityInput" placeholder="Enter city name">
            <button id="searchButton">Search</button>
        </div>
        <div id="weatherInfo">
            <!-- Weather information will be displayed here -->
        </div>
    </div>
    <script src="dist/index.js"></script>
</body>
</html>

This HTML provides the basic structure for the app, including a title, a search input, a search button, and a container to display weather information. The `<script>` tag at the end links to the compiled JavaScript file.

Styling with CSS (style.css)

Now, let’s add some basic styling to our app. Open `src/style.css` and add the following CSS rules:

body {
    font-family: sans-serif;
    background-color: #f0f0f0;
    margin: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
}

.container {
    background-color: #fff;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    text-align: center;
}

h1 {
    color: #333;
}

.search {
    margin-bottom: 20px;
}

input[type="text"] {
    padding: 8px;
    border-radius: 4px;
    border: 1px solid #ccc;
    margin-right: 10px;
}

button {
    padding: 8px 15px;
    border-radius: 4px;
    border: none;
    background-color: #007bff;
    color: white;
    cursor: pointer;
}

#weatherInfo {
    margin-top: 20px;
}

This CSS provides basic styling for the layout, fonts, colors, and input elements. Feel free to customize the styling to your preference.

Writing the TypeScript Code (src/index.ts)

This is the core of our application where we will write the TypeScript code. Open `src/index.ts` and start by importing the necessary elements and defining some types.

// src/index.ts

// Define types for the weather data
interface WeatherData {
  name: string;
  main: {
    temp: number;
    humidity: number;
  };
  weather: [{
    description: string;
    icon: string;
  }];
  wind: {
    speed: number;
  };
}

// Get references to HTML elements
const cityInput = document.getElementById('cityInput') as HTMLInputElement;
const searchButton = document.getElementById('searchButton') as HTMLButtonElement;
const weatherInfo = document.getElementById('weatherInfo') as HTMLDivElement;

// Replace with your OpenWeatherMap API key
const apiKey = 'YOUR_API_KEY';

Here’s a breakdown of this code:

  • WeatherData interface: This defines the structure of the weather data we’ll receive from the API. This is crucial for type safety in TypeScript.
  • Getting HTML elements: We get references to the input field, search button, and the weather information display area using document.getElementById().
  • apiKey: This is where you’ll store your OpenWeatherMap API key. You will need to sign up for a free account on openweathermap.org and obtain an API key. Remember to replace 'YOUR_API_KEY' with your actual key.

Now, let’s create a function to fetch weather data from the OpenWeatherMap API.

async function getWeatherData(city: string): Promise<WeatherData | null> {
  const apiUrl = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`;

  try {
    const response = await fetch(apiUrl);

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const data: WeatherData = await response.json();
    return data;

  } catch (error: any) {
    console.error('Error fetching weather data:', error);
    alert('Error fetching weather data. Please check the city name and your API key.');
    return null;
  }
}

Explanation:

  • getWeatherData function: This asynchronous function takes a city name as input and fetches weather data from the OpenWeatherMap API.
  • API URL: It constructs the API URL using the city name and your API key. The units=metric part ensures the temperature is in Celsius.
  • fetch: It uses the fetch API to make a GET request to the OpenWeatherMap API.
  • Error Handling: It includes error handling to check for HTTP errors and catches any other errors that might occur during the fetch operation. It also displays an alert to the user.
  • Return Value: It returns either the weather data (of type WeatherData) or null if an error occurs.

Next, let’s create a function to display the weather information in the HTML.


function displayWeather(data: WeatherData) {
    if (!data) {
        weatherInfo.innerHTML = '<p>City not found or error fetching data.</p>';
        return;
    }

    const { name, main, weather, wind } = data;
    const iconCode = weather[0].icon;
    const iconUrl = `http://openweathermap.org/img/wn/${iconCode}.png`;

    weatherInfo.innerHTML = `
        <h2>${name}</h2>
        <img src="${iconUrl}" alt="Weather Icon">
        <p>Temperature: ${main.temp} °C</p>
        <p>Humidity: ${main.humidity}%</p>
        <p>Description: ${weather[0].description}</p>
        <p>Wind Speed: ${wind.speed} m/s</p>
    `;
}

Explanation:

  • displayWeather function: This function takes the weather data as input and updates the HTML to display the weather information.
  • Error Handling: It checks if the data is null and displays an appropriate message if it is.
  • Destructuring: It uses destructuring to extract relevant data from the WeatherData object for cleaner code.
  • Dynamic Content: It dynamically generates the HTML content to display the city name, temperature, humidity, weather description, and wind speed. It also includes an image of the weather icon.

Finally, let’s add an event listener to the search button to trigger the weather data fetching process.


searchButton.addEventListener('click', async () => {
    const city = cityInput.value;
    if (city) {
        const data = await getWeatherData(city);
        if (data) {
            displayWeather(data);
        }
    }
});

Explanation:

  • Event Listener: This adds a click event listener to the search button.
  • Get City: When the button is clicked, it retrieves the city name from the input field.
  • Call Functions: It calls the getWeatherData function to fetch the weather data and then calls the displayWeather function to display the data in the HTML.

Compiling and Running the App

Now that we have written the code, let’s compile and run the application:

  1. Compile the TypeScript code: Open your terminal and run the following command from your project directory:
    tsc

    This command compiles the TypeScript code into JavaScript and creates a `dist` folder containing the compiled `index.js` file.

  2. Open the HTML file: Open `index.html` in your web browser. You can either open it directly from your file system or serve it using a simple web server.
    • Directly from the file system: Double-click `index.html` to open it in your browser. However, some browsers may block API requests if the HTML is opened directly from the file system.
    • Using a simple web server: This is the recommended approach. You can use a simple web server like `http-server` (install it globally using `npm install -g http-server`). Then, navigate to your project directory in the terminal and run:
      http-server

      This will start a web server and provide a local URL (usually `http://localhost:8080`) that you can open in your browser.

  3. Test the app: Enter a city name in the input field and click the “Search” button. If everything is set up correctly, the weather information for the city should be displayed.

Common Mistakes and How to Fix Them

Here are some common mistakes and how to fix them when building a weather app with TypeScript:

  • Incorrect API Key:
    • Mistake: Using an incorrect or invalid API key.
    • Fix: Double-check your API key and ensure it is correct. Also, verify that you have enabled the “Current Weather Data” API in your OpenWeatherMap account.
  • CORS Issues:
    • Mistake: Encountering Cross-Origin Resource Sharing (CORS) errors, which prevent your JavaScript code from making requests to the OpenWeatherMap API from your local development environment.
    • Fix: If you are running into CORS issues, you can use a CORS proxy. There are several free CORS proxy services available online. You can modify your API URL to point to the proxy, e.g., `https://cors-anywhere.herokuapp.com/https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`. Note that using a public CORS proxy might have rate limits or security concerns, so it’s best for development and testing. For production, consider setting up your own proxy server or using a server-side solution to make the API requests.
  • Typos in City Names:
    • Mistake: Typing the city name incorrectly in the input field.
    • Fix: Implement error handling to inform the user if the city is not found or the API request fails. Consider adding features like auto-complete or suggestions to help users enter city names correctly.
  • Incorrect HTML Element References:
    • Mistake: Referencing HTML elements with incorrect IDs or class names in your TypeScript code.
    • Fix: Carefully check the IDs and class names in your HTML and ensure they match the references in your TypeScript code.
  • Missing API Key:
    • Mistake: Forgetting to replace ‘YOUR_API_KEY’ with your actual API key.
    • Fix: Ensure that you have replaced the placeholder API key with your valid API key in the `src/index.ts` file.

Key Takeaways

  • Type Safety: TypeScript significantly improves code quality and maintainability by enforcing type checking.
  • Asynchronous Operations: Understanding how to work with async/await and Promises is crucial for handling API calls.
  • API Integration: Learning how to fetch and process data from external APIs is a fundamental skill for web development.
  • User Interface: Creating a basic UI with HTML, CSS, and JavaScript is essential for building interactive web applications.
  • Error Handling: Implementing robust error handling is important to make your app user-friendly and reliable.

FAQ

  1. What is TypeScript?

    TypeScript is a superset of JavaScript that adds static typing. It allows you to catch errors during development, improving code quality and maintainability.

  2. Why use TypeScript for a weather app?

    TypeScript helps catch potential errors early on, especially when working with external APIs and complex data structures. It also improves code readability and makes it easier to refactor and maintain the codebase.

  3. Where can I get an API key for OpenWeatherMap?

    You can get a free API key by signing up for an account on openweathermap.org.

  4. How can I deploy this weather app?

    You can deploy this app to platforms like Netlify, Vercel, or GitHub Pages. You’ll need to build the project (using tsc) and then deploy the contents of the `dist` folder (along with your `index.html` and any other assets) to the platform of your choice.

  5. Can I add more features to this app?

    Yes! You can add features like: displaying the weather forecast for multiple days, adding a map to show the location, allowing users to save their favorite cities, and implementing more advanced styling and user interface components.

This simple weather application serves as a stepping stone to understanding the power of TypeScript in real-world web development. By building upon this foundation, you can explore more advanced features, refine your skills, and create more sophisticated web applications. The combination of TypeScript’s type safety and the ability to interact with external APIs makes it a powerful tool for modern web development. With practice and experimentation, you’ll be well-equipped to build not just a weather app, but a wide range of interactive and informative web applications.