TypeScript Tutorial: Creating a Simple Currency Converter

In today’s globalized world, dealing with different currencies is a common occurrence. Whether you’re traveling, shopping online, or managing international finances, having a quick and reliable way to convert currencies is incredibly useful. This tutorial will guide you through building a simple currency converter using TypeScript. We’ll cover everything from setting up your project to fetching real-time exchange rates and displaying the converted amounts. By the end, you’ll have a functional application and a solid understanding of how to use TypeScript to create practical, real-world solutions.

Why TypeScript?

TypeScript, a superset of JavaScript, brings static typing to your code. This means you define the data types of your variables, function parameters, and return values. This offers several benefits:

  • Early Error Detection: TypeScript catches type-related errors during development, preventing runtime surprises.
  • Improved Code Readability: Type annotations make your code easier to understand and maintain.
  • Enhanced Developer Experience: IDEs can provide better autocompletion, refactoring, and navigation.
  • Scalability: TypeScript helps you manage larger projects more effectively.

Setting Up Your Project

Let’s get started by setting up our project. You’ll need Node.js and npm (Node Package Manager) installed on your system. If you don’t have them, download and install them from the official Node.js website.

  1. Create a Project Directory: Open your terminal or command prompt and create a new directory for your project:
    mkdir currency-converter
    cd currency-converter
  2. Initialize npm: Initialize a new npm project:
    npm init -y

    This creates a package.json file, which manages your project’s dependencies and scripts.

  3. Install TypeScript: Install TypeScript globally or locally. For this tutorial, we’ll install it locally as a development dependency:
    npm install typescript --save-dev
  4. Create a TypeScript Configuration File: Generate a tsconfig.json file to configure TypeScript:
    npx tsc --init

    This creates a tsconfig.json file with default settings. You can customize this file to control how TypeScript compiles your code. We’ll make a few adjustments shortly.

  5. Create Source Files: Create a directory named src and a file named index.ts inside it. This is where we’ll write our TypeScript code:
    mkdir src
    touch src/index.ts

Configuring TypeScript

Open your tsconfig.json file and make the following changes to customize the compiler’s behavior. These settings will help ensure our code is compiled correctly and that we catch potential issues early.

{
  "compilerOptions": {
    "target": "es6", /* Specify ECMAScript target version */
    "module": "commonjs", /* Specify module code generation */
    "outDir": "./dist", /* Redirect output structure to the directory */
    "rootDir": "./src", /* Specify the root directory of input files */
    "strict": true, /* Enable all strict type-checking options. */
    "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. */
    "skipLibCheck": true, /* Skip type checking all .d.ts files. */
    "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
  },
  "include": ["src/**/*"]
}

Here’s a breakdown of what these settings do:

  • target: "es6": Specifies that the compiled JavaScript should be compatible with ES6 (ECMAScript 2015) features.
  • module: "commonjs": Specifies the module system to use. CommonJS is suitable for Node.js environments.
  • outDir: "./dist": Specifies the output directory for the compiled JavaScript files.
  • rootDir: "./src": Specifies the root directory of your TypeScript source files.
  • strict: true: Enables strict type-checking, which is highly recommended for catching errors.
  • esModuleInterop: true: Helps with importing modules from different module systems.
  • skipLibCheck: true: Skips type checking of declaration files (.d.ts files) which can speed up compilation.
  • forceConsistentCasingInFileNames: true: Enforces consistent casing in file names to prevent potential issues.
  • include: ["src/**/*"]: Tells the compiler to include all TypeScript files within the src directory.

Fetching Exchange Rates

To convert currencies, we need real-time exchange rates. We’ll use a free API for this purpose. There are several options available; for this tutorial, we’ll use the ExchangeRate-API (https://www.exchangerate-api.com/). You can sign up for a free API key (which is recommended for higher rate limits), or use the API without a key, but with lower rate limits. Remember to check the API’s terms of service and rate limits.

First, let’s install the node-fetch package to make HTTP requests. This is a lightweight package that allows us to fetch data from APIs in Node.js.

npm install node-fetch

Now, let’s write the code to fetch exchange rates in src/index.ts:

import fetch from 'node-fetch';

// Replace with your API key if you have one
const API_KEY = '';

// Function to fetch exchange rates
async function getExchangeRate(fromCurrency: string, toCurrency: string): Promise {
  const baseUrl = 'https://v6.exchangerate-api.com/v6';
  const apiKeyPart = API_KEY ? `/${API_KEY}` : ''; // API Key is optional
  const url = `${baseUrl}${apiKeyPart}/pair/${fromCurrency}/${toCurrency}`;

  try {
    const response = await fetch(url);

    if (!response.ok) {
      console.error(`API request failed with status ${response.status}`);
      return null;
    }

    const data = await response.json();
    return data.conversion_rate;

  } catch (error: any) {
    console.error('Error fetching exchange rate:', error.message);
    return null;
  }
}

// Example usage (will be called later)
async function convertCurrency(fromCurrency: string, toCurrency: string, amount: number): Promise {
  const rate = await getExchangeRate(fromCurrency, toCurrency);
  if (rate === null) {
    return null;
  }
  return amount * rate;
}

Let’s break down this code:

  • We import the fetch function from the node-fetch package.
  • We define an API_KEY variable. This is where you would put your API key from ExchangeRate-API if you have one. It’s left blank by default, allowing you to use the API without one (with rate limits).
  • The getExchangeRate function takes two currency codes (e.g., “USD”, “EUR”) as input and returns the exchange rate as a number or null if an error occurs.
  • The function constructs the API URL, including the API key if provided.
  • It uses fetch to make a GET request to the API.
  • It checks if the response is okay (status code 200-299). If not, it logs an error and returns null.
  • It parses the JSON response and returns the conversion_rate.
  • The convertCurrency function takes the from currency, to currency, and the amount to convert. It then calls getExchangeRate to get the exchange rate and returns the converted amount.

Building the User Interface (UI)

For this tutorial, we will create a very basic command-line interface (CLI) to interact with our currency converter. This keeps the focus on the TypeScript and API interaction and avoids the complexities of building a full-fledged web or desktop UI. If you’d like to build a web-based UI, you could use HTML, CSS, and JavaScript, along with a framework like React, Angular, or Vue.js.

Let’s add some code to src/index.ts to handle user input and display the results:

import fetch from 'node-fetch';
import * as readline from 'readline';

// Replace with your API key if you have one
const API_KEY = '';

// Function to fetch exchange rates (same as before)
async function getExchangeRate(fromCurrency: string, toCurrency: string): Promise {
  const baseUrl = 'https://v6.exchangerate-api.com/v6';
  const apiKeyPart = API_KEY ? `/${API_KEY}` : ''; // API Key is optional
  const url = `${baseUrl}${apiKeyPart}/pair/${fromCurrency}/${toCurrency}`;

  try {
    const response = await fetch(url);

    if (!response.ok) {
      console.error(`API request failed with status ${response.status}`);
      return null;
    }

    const data = await response.json();
    return data.conversion_rate;

  } catch (error: any) {
    console.error('Error fetching exchange rate:', error.message);
    return null;
  }
}

// Example usage (will be called later)
async function convertCurrency(fromCurrency: string, toCurrency: string, amount: number): Promise {
  const rate = await getExchangeRate(fromCurrency, toCurrency);
  if (rate === null) {
    return null;
  }
  return amount * rate;
}

// Function to get user input
async function getUserInput(prompt: string): Promise {
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });

  return new Promise((resolve) => {
    rl.question(prompt, (answer) => {
      rl.close();
      resolve(answer);
    });
  });
}

// Main function to run the converter
async function main() {
  try {
    const fromCurrency = await getUserInput('Enter the source currency (e.g., USD): ');
    const toCurrency = await getUserInput('Enter the target currency (e.g., EUR): ');
    const amountStr = await getUserInput('Enter the amount to convert: ');
    const amount = parseFloat(amountStr);

    if (isNaN(amount)) {
      console.error('Invalid amount. Please enter a number.');
      return;
    }

    const convertedAmount = await convertCurrency(fromCurrency.toUpperCase(), toCurrency.toUpperCase(), amount);

    if (convertedAmount === null) {
      console.error('Could not convert the currency. Please check the currency codes and your internet connection.');
      return;
    }

    console.log(`${amount} ${fromCurrency.toUpperCase()} is equal to ${convertedAmount.toFixed(2)} ${toCurrency.toUpperCase()}`);

  } catch (error: any) {
    console.error('An unexpected error occurred:', error.message);
  }
}

// Run the main function
main();

Here’s a breakdown of the UI code:

  • We import the readline module to handle user input from the command line.
  • The getUserInput function prompts the user with a message and waits for their input. It uses readline.createInterface to set up the input and output streams and returns a promise that resolves with the user’s answer.
  • The main function is the core of our application.
  • It calls getUserInput to get the source currency, target currency, and the amount to convert.
  • It uses parseFloat to convert the amount from a string to a number. It also validates that the input is actually a number.
  • It calls the convertCurrency function to perform the conversion. We convert the currency codes to uppercase to handle different input formats.
  • It displays the converted amount to the user, formatted to two decimal places.
  • Error handling is included to catch invalid input or API errors.
  • Finally, the main() function is called to start the application.

Compiling and Running the Application

Now that we have our code, let’s compile and run it.

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

    This command uses the TypeScript compiler (tsc) to compile your .ts files into .js files in the dist directory.

  2. Run the application: Execute the compiled JavaScript file using Node.js:
    node dist/index.js

    The application will prompt you to enter the source currency, target currency, and the amount to convert.

  3. Test the application: Enter the requested information and verify that the application correctly converts the currency. Try different currency pairs and amounts to ensure it works as expected.

Common Mistakes and How to Fix Them

Here are some common mistakes and how to avoid them:

  • Incorrect API Key: If you’re using an API key, double-check that you’ve entered it correctly. Typos are a common cause of errors. Remember that if you use the API without a key, you may have rate limits.
  • Incorrect Currency Codes: Make sure you’re using the correct currency codes (e.g., USD, EUR, GBP). Case sensitivity can sometimes be an issue, so it’s a good practice to convert currency codes to uppercase before using them.
  • Network Issues: Ensure you have a stable internet connection. If the API request fails, the application won’t be able to fetch the exchange rate.
  • Type Errors: TypeScript’s type checking can help prevent runtime errors. Review any TypeScript errors reported during compilation and fix them. For example, make sure you are passing the correct data types to functions.
  • Uncaught Errors: Always handle potential errors, such as network errors or invalid API responses. Use try...catch blocks to gracefully handle exceptions and provide informative error messages to the user.
  • Incorrect File Paths: Double-check the file paths when importing modules or accessing files. Ensure the paths are relative to the current file.

Key Takeaways

  • TypeScript Basics: This tutorial provided a practical introduction to TypeScript, including type annotations, interfaces, and working with modules.
  • API Integration: You learned how to fetch data from a REST API using node-fetch.
  • User Input: You used the readline module to create a simple command-line interface for user interaction.
  • Error Handling: You implemented basic error handling to make your application more robust.
  • Project Structure: You organized your project files and used a tsconfig.json file to configure the TypeScript compiler.

FAQ

Here are some frequently asked questions:

  1. Can I use a different API? Yes, you can. There are many free and paid currency exchange rate APIs available. Just make sure to adapt the code to match the API’s endpoints and data format.
  2. How can I improve the UI? For a more user-friendly interface, you could build a web application using HTML, CSS, and JavaScript, along with a framework like React, Angular, or Vue.js. You could also use a library like inquirer for a more interactive command-line interface.
  3. How can I add more currencies? You can extend the application to support more currencies by modifying the UI to allow the user to select from a list of available currencies. You would also need to update the API calls to handle the new currencies.
  4. How do I handle API rate limits? If the API has rate limits (as most do), you should implement logic to handle them. This might include caching exchange rates, retrying requests, or using a different API if you exceed the limits.
  5. Can I store the exchange rates? Yes, you can store the exchange rates in a database or a file. This will allow you to access the exchange rates even when you do not have access to the internet, or to reduce the number of API calls. You would need to add code to read and write the exchange rates to the storage mechanism.

This tutorial provides a solid foundation for building a currency converter. You can expand upon this by adding features such as support for more currencies, historical exchange rates, and a more sophisticated user interface. The beauty of TypeScript lies in its ability to help you write cleaner, more maintainable code, making it easier to build complex applications. By understanding the core concepts presented here, you’re well-equipped to tackle more advanced TypeScript projects and build applications that solve real-world problems. The principles of type safety, modularity, and error handling are crucial for developing robust and scalable software. Applying these principles consistently will not only improve your code quality but also streamline your development process. Keep experimenting, keep learning, and explore the endless possibilities that TypeScript unlocks.