TypeScript Tutorial: Creating a Simple Interactive Currency Converter

In today’s interconnected world, dealing with different currencies is a common occurrence. Whether you’re traveling, shopping online, or managing international finances, understanding currency exchange rates is crucial. Manually converting currencies can be time-consuming and prone to errors. This tutorial will guide you through building a simple, interactive currency converter using TypeScript, empowering you to effortlessly convert between different currencies.

Why Build a Currency Converter?

Creating a currency converter offers several advantages:

  • Practicality: Easily convert currencies for personal or professional use.
  • Learning: Gain hands-on experience with TypeScript, a powerful language for building robust applications.
  • Customization: Tailor the converter to your specific needs, adding features like historical exchange rates or multiple currency support.

Prerequisites

Before we begin, ensure you have the following:

  • Basic knowledge of HTML, CSS, and JavaScript: Familiarity with these web technologies is essential.
  • Node.js and npm (Node Package Manager) installed: These are required to set up and manage our project.
  • A code editor: Choose your preferred editor, such as Visual Studio Code, Sublime Text, or Atom.
  • TypeScript installed globally: You can install it using npm: npm install -g typescript

Setting Up the Project

Let’s start by creating a new project directory and initializing it with npm:

  1. Create a new directory for your project (e.g., currency-converter).
  2. Navigate to the directory in your terminal.
  3. Run npm init -y to initialize a new npm project. This will create a package.json file.
  4. Create a src directory to hold our TypeScript files.
  5. Create an index.html file in the root directory.

Configuring TypeScript

Next, we need to configure TypeScript for our project. Create a tsconfig.json file in the root directory. This file tells the TypeScript compiler how to compile your code. Here’s a basic configuration:

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

Explanation of the options:

  • target: Specifies the JavaScript version to compile to (ES5 is widely supported).
  • module: Specifies the module system (CommonJS is suitable for Node.js).
  • outDir: Defines where the compiled JavaScript files will be placed.
  • strict: Enables strict type checking.
  • esModuleInterop: Enables interoperability between CommonJS and ES modules.
  • skipLibCheck: Skips type checking of declaration files.
  • forceConsistentCasingInFileNames: Enforces consistent casing in filenames.
  • include: Specifies which files to include in the compilation.

Creating the HTML Structure

Let’s create the basic HTML structure for our currency converter in index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Currency Converter</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="converter-container">
        <h1>Currency Converter</h1>
        <div class="input-group">
            <label for="amount">Amount:</label>
            <input type="number" id="amount" placeholder="Enter amount">
        </div>
        <div class="input-group">
            <label for="fromCurrency">From:</label>
            <select id="fromCurrency">
                <!-- Currencies will be added here dynamically -->
            </select>
        </div>
        <div class="input-group">
            <label for="toCurrency">To:</label>
            <select id="toCurrency">
                <!-- Currencies will be added here dynamically -->
            </select>
        </div>
        <button id="convertButton">Convert</button>
        <div id="result"></div>
    </div>
    <script src="dist/index.js"></script>
</body>
</html>

This HTML provides the basic layout:

  • A container div with the class “converter-container” to hold all the elements.
  • An input field for the amount to convert.
  • Two select elements for selecting the currencies (from and to).
  • A button to trigger the conversion.
  • A div to display the conversion result.

Styling with CSS

Create a style.css file in your project’s root directory and add some basic styling to make the converter visually appealing:

.converter-container {
    width: 300px;
    margin: 50px auto;
    padding: 20px;
    border: 1px solid #ccc;
    border-radius: 5px;
    text-align: center;
}

.input-group {
    margin-bottom: 15px;
}

label {
    display: block;
    margin-bottom: 5px;
}

input[type="number"], select {
    width: 100%;
    padding: 8px;
    border: 1px solid #ddd;
    border-radius: 4px;
    box-sizing: border-box;
    margin-bottom: 10px;
}

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

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

#result {
    margin-top: 20px;
    font-weight: bold;
}

Writing the TypeScript Code

Now, let’s write the TypeScript code in src/index.ts. This is where the core logic of our currency converter will reside.

// src/index.ts

// Define an interface for the currency data
interface CurrencyData {
    code: string;
    name: string;
}

// API endpoint for currency exchange rates (replace with your API)
const API_ENDPOINT = 'https://api.exchangerate-api.com/v4/latest/USD'; // Example: using USD as base

// Function to fetch currency data from the API
async function fetchCurrencies(): Promise<{ [code: string]: number } | null> {
    try {
        const response = await fetch(API_ENDPOINT);
        const data = await response.json();

        // Extract rates, assuming the API returns rates based on USD
        if (data && data.rates) {
            return data.rates;
        }

        console.error('Failed to fetch currency rates.');
        return null;
    } catch (error) {
        console.error('Error fetching currency data:', error);
        return null;
    }
}

// Function to populate the currency select options
async function populateCurrencies(fromSelect: HTMLSelectElement, toSelect: HTMLSelectElement) {
    const rates = await fetchCurrencies();

    if (!rates) {
        // Handle the error (e.g., display an error message to the user)
        console.error('Failed to load currency data.');
        return;
    }

    // Get the currency codes and sort them alphabetically
    const currencyCodes = Object.keys(rates).sort();

    // Function to create an option element
    function createOption(currencyCode: string): HTMLOptionElement {
        const option = document.createElement('option');
        option.value = currencyCode;
        option.textContent = currencyCode;
        return option;
    }

    // Populate the 'from' select element
    currencyCodes.forEach(code => {
        fromSelect.appendChild(createOption(code));
    });

    // Populate the 'to' select element
    currencyCodes.forEach(code => {
        toSelect.appendChild(createOption(code));
    });
}

// Function to perform the currency conversion
async function convertCurrency() {
    const amountInput = document.getElementById('amount') as HTMLInputElement;
    const fromCurrencySelect = document.getElementById('fromCurrency') as HTMLSelectElement;
    const toCurrencySelect = document.getElementById('toCurrency') as HTMLSelectElement;
    const resultDiv = document.getElementById('result') as HTMLDivElement;

    const amount = parseFloat(amountInput.value);
    const fromCurrency = fromCurrencySelect.value;
    const toCurrency = toCurrencySelect.value;

    if (isNaN(amount)) {
        resultDiv.textContent = 'Please enter a valid amount.';
        return;
    }

    const rates = await fetchCurrencies();

    if (!rates) {
        resultDiv.textContent = 'Failed to fetch currency rates.';
        return;
    }

    // Handle the case where the fromCurrency is the same as the toCurrency
    if (fromCurrency === toCurrency) {
        resultDiv.textContent = amount.toFixed(2) + ' ' + fromCurrency;
        return;
    }

    // Convert from USD to the 'from' currency
    const fromRate = rates[fromCurrency];
    const toRate = rates[toCurrency];

    if (!fromRate || !toRate) {
        resultDiv.textContent = 'Currency rates not available.';
        return;
    }

    // Calculate the converted amount
    const convertedAmount = (amount / fromRate) * toRate;

    resultDiv.textContent = `${amount.toFixed(2)} ${fromCurrency} = ${convertedAmount.toFixed(2)} ${toCurrency}`;
}

// Main function to initialize the app
async function main() {
    const fromCurrencySelect = document.getElementById('fromCurrency') as HTMLSelectElement;
    const toCurrencySelect = document.getElementById('toCurrency') as HTMLSelectElement;
    const convertButton = document.getElementById('convertButton') as HTMLButtonElement;

    // Populate currency options
    await populateCurrencies(fromCurrencySelect, toCurrencySelect);

    // Add event listener to the convert button
    convertButton.addEventListener('click', convertCurrency);
}

// Run the main function when the DOM is loaded
document.addEventListener('DOMContentLoaded', main);

Let’s break down the code:

  • Interfaces and Types: We define an interface CurrencyData to represent currency information.
  • API Endpoint: We define the API endpoint to fetch the exchange rates. Important: You’ll need to replace the placeholder API endpoint (https://api.exchangerate-api.com/v4/latest/USD) with a real currency exchange rate API. There are free APIs available; search for “free currency exchange rate API”. Some APIs require an API key; make sure to handle the key securely (e.g., using environment variables).
  • fetchCurrencies(): This asynchronous function fetches currency data from the API and returns an object containing the exchange rates. It includes error handling. The API response is assumed to provide rates relative to USD.
  • populateCurrencies(): This function fetches the currency data and populates the dropdown select elements with the available currency options.
  • convertCurrency(): This function performs the currency conversion. It retrieves the input values (amount, from currency, to currency), fetches the exchange rates, calculates the converted amount, and displays the result. It also includes error handling (e.g., checking for invalid input and missing rates).
  • main(): This function initializes the application by calling populateCurrencies() to populate the dropdowns and attaching an event listener to the convert button.
  • Event Listener: The `DOMContentLoaded` event listener ensures that the main() function runs after the HTML document has been fully loaded.

Compiling and Running the Application

Now, let’s compile the TypeScript code and run the application:

  1. Compile the TypeScript code: Open your terminal, navigate to your project directory, and run tsc. This will compile the TypeScript code into JavaScript and place the output in the dist folder.
  2. Open index.html in your browser: You can simply double-click the file, or use a local web server (recommended for development).
  3. Test the application: Enter an amount, select currencies, and click the “Convert” button. The converted amount should be displayed.

Common Mistakes and How to Fix Them

Here are some common mistakes and how to avoid them:

  • Incorrect API Endpoint: Ensure you are using a valid and reliable currency exchange rate API. Double-check the API documentation for the correct endpoint and parameters.
  • Handling API Errors: Implement robust error handling to gracefully handle API failures. Display informative error messages to the user.
  • Missing or Incorrect Data Types: Use TypeScript’s type checking to catch type-related errors early. Define interfaces or types for your data structures.
  • Asynchronous Operations: Remember to use async/await or Promises to handle asynchronous operations (like fetching data from the API) correctly.
  • Cross-Origin Resource Sharing (CORS) Issues: If you encounter CORS errors, the API may not allow requests from your domain. Consider using a proxy server or a different API.
  • Incorrect Calculation: Double-check your calculation logic, especially the division/multiplication order when converting between currencies.
  • Not Updating the UI: Make sure you are correctly updating the DOM with the converted amount. Use the `textContent` property of the result div to display the converted value.

Enhancements and Further Development

You can enhance the currency converter further by adding features like:

  • Historical Exchange Rates: Display historical exchange rates for trend analysis.
  • Multiple Currency Support: Allow the user to convert multiple currencies at once.
  • Currency Symbols: Display currency symbols alongside the amounts.
  • User Interface Improvements: Improve the user interface with better styling and layout.
  • Error Handling: Implement more robust error handling and user feedback.
  • Saving User Preferences: Allow users to save their preferred currencies.

Key Takeaways

  • TypeScript enhances code quality and maintainability through static typing.
  • Asynchronous operations are crucial when working with APIs.
  • Error handling is essential for creating robust applications.
  • User interface elements can be easily manipulated using JavaScript.
  • This tutorial provides a solid foundation for building more complex web applications.

FAQ

Here are some frequently asked questions:

  1. How do I choose a currency exchange rate API?

    Search online for “free currency exchange rate API”. Consider factors like reliability, data accuracy, API limits, and ease of use. Ensure the API supports the currencies you need.

  2. How can I handle API rate limits?

    Some APIs have rate limits. Implement logic to handle these limits, such as using a caching mechanism, implementing retry logic with exponential backoff, or using a proxy server.

  3. How do I deploy this application?

    You can deploy this application by hosting the HTML, CSS, and JavaScript files on a web server or a platform like Netlify or Vercel. Make sure your API calls are configured correctly for the deployment environment.

  4. Can I use this code in a React or Angular application?

    Yes, you can adapt the core logic of this currency converter to React or Angular. You would need to integrate it into the component structure of your chosen framework. The fundamental TypeScript concepts would remain the same.

Building a currency converter in TypeScript provides a practical and educational experience. You’ve learned about setting up a TypeScript project, interacting with external APIs, handling user input, and displaying results. You can now use this foundation to build more sophisticated applications and explore the power of TypeScript in web development. The journey of learning never truly ends; the more you practice, experiment, and build, the better you become. Every line of code, every bug fixed, every feature implemented, moves you closer to mastery.