In today’s interconnected world, the ability to convert currencies on the fly is more important than ever. Whether you’re planning a trip abroad, managing international finances, or simply curious about exchange rates, a currency converter is an invaluable tool. In this tutorial, we’ll dive into building a simple, yet functional, web-based currency converter using TypeScript. We’ll explore the core concepts, walk through the code step-by-step, and equip you with the knowledge to create your own currency conversion application. This tutorial is designed for beginners and intermediate developers, so no prior experience with TypeScript is strictly required, although a basic understanding of HTML, CSS, and JavaScript will be helpful.
Why Build a Currency Converter?
Creating a currency converter is an excellent way to learn and practice TypeScript. It allows you to:
- Understand and apply fundamental TypeScript concepts like types, interfaces, and classes.
- Work with asynchronous operations and API calls to fetch real-time exchange rates.
- Gain experience in building interactive web applications.
- Create a practical tool that you can use in your daily life.
Beyond the technical skills, building a currency converter provides a tangible project that you can showcase in your portfolio, demonstrating your proficiency in TypeScript and web development.
Setting Up Your Development Environment
Before we start coding, let’s set up our development environment. You’ll need the following:
- Node.js and npm (or yarn): Node.js provides the JavaScript runtime environment, and npm (Node Package Manager) or yarn is used to manage project dependencies. You can download them from https://nodejs.org/.
- A Code Editor: Choose a code editor like Visual Studio Code (VS Code), Atom, Sublime Text, or any other editor you prefer. VS Code is highly recommended due to its excellent TypeScript support.
- TypeScript Compiler: We’ll install the TypeScript compiler globally using npm:
npm install -g typescript
Once you’ve installed these, create a new project directory for your currency converter:
mkdir currency-converter
cd currency-converter
Initialize a new npm project:
npm init -y
This will create a package.json file in your project directory.
Project Structure
Let’s define a basic project structure to keep our code organized:
currency-converter/
├── src/
│ ├── index.ts # Main application logic
│ ├── components/
│ │ ├── CurrencyInput.ts # Component for currency input
│ │ └── ... # Other components (if any)
├── index.html # HTML file
├── style.css # CSS file
├── tsconfig.json # TypeScript configuration
└── package.json # Project dependencies
Configuring TypeScript
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": "es6",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"]
}
Explanation of the options:
target: Specifies the JavaScript version to compile to (e.g., “es5”, “es6”, “esnext”).module: Specifies the module system to use (e.g., “commonjs”, “es6”).outDir: Specifies the output directory for compiled JavaScript files.strict: Enables strict type checking.esModuleInterop: Enables interoperability between CommonJS and ES modules.skipLibCheck: Skips type checking of declaration files (e.g., from node_modules).forceConsistentCasingInFileNames: Enforces consistent casing in file names.include: Specifies the files to include in the compilation.
Creating the HTML Structure (index.html)
Create an index.html file in your project root. This will be the basic structure of your web application:
<!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="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 dynamically populated here -->
</select>
</div>
<div class="input-group">
<label for="toCurrency">To:</label>
<select id="toCurrency">
<!-- Currencies will be dynamically populated here -->
</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 structure: a title, input fields for the amount, from and to currencies (using select elements), a button to trigger the conversion, and a div to display the result. We’ve linked a CSS file (`style.css`) for styling and a JavaScript file (`dist/index.js`) which we’ll generate from our TypeScript code.
Styling with CSS (style.css)
Create a style.css file in your project root to add some basic styling:
body {
font-family: sans-serif;
background-color: #f4f4f4;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
}
.container {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 80%;
max-width: 400px;
}
h1 {
text-align: center;
margin-bottom: 20px;
}
.input-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input[type="number"], select {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
margin-bottom: 10px;
}
button {
background-color: #4CAF50;
color: white;
padding: 12px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
width: 100%;
}
button:hover {
background-color: #3e8e41;
}
#result {
margin-top: 15px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
text-align: center;
}
This CSS provides basic styling for the layout, input fields, and the result display. Feel free to customize the styles to your liking.
Writing the TypeScript Code (src/index.ts)
Now, let’s write the TypeScript code for our currency converter. Create an index.ts file in the src directory. This is where the main logic of your application will reside.
// Define an interface for the exchange rates
interface ExchangeRates {
[currencyCode: string]: number;
}
// Define an interface for the API response
interface ApiResponse {
rates: ExchangeRates;
base: string;
date: string;
}
// API endpoint for fetching exchange rates (replace with a real API)
const API_URL = 'https://api.exchangerate-api.com/v4/latest/USD'; // Example API, use your preferred API
// DOM elements
const amountInput = document.getElementById('amount') as HTMLInputElement;
const fromCurrencySelect = document.getElementById('fromCurrency') as HTMLSelectElement;
const toCurrencySelect = document.getElementById('toCurrency') as HTMLSelectElement;
const convertButton = document.getElementById('convertButton') as HTMLButtonElement;
const resultDiv = document.getElementById('result') as HTMLDivElement;
// Function to fetch exchange rates
async function getExchangeRates(): Promise<ApiResponse> {
try {
const response = await fetch(API_URL);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error: any) {
console.error('Could not fetch exchange rates:', error);
throw error; // Re-throw to handle it later
}
}
// Function to populate currency options
async function populateCurrencyOptions() {
try {
const data = await getExchangeRates();
const rates = data.rates;
const currencies = Object.keys(rates);
currencies.forEach(currency => {
const option = document.createElement('option');
option.value = currency;
option.textContent = currency;
fromCurrencySelect.appendChild(option.cloneNode(true)); // Add to both selects
toCurrencySelect.appendChild(option);
});
} catch (error) {
resultDiv.textContent = 'Error loading currencies.';
console.error('Error populating currency options:', error);
}
}
// Function to convert currencies
async function convertCurrency() {
try {
const amount = parseFloat(amountInput.value);
const fromCurrency = fromCurrencySelect.value;
const toCurrency = toCurrencySelect.value;
if (isNaN(amount) || amount <= 0) {
resultDiv.textContent = 'Please enter a valid amount.';
return;
}
const data = await getExchangeRates();
const rates = data.rates;
if (!rates[fromCurrency] || !rates[toCurrency]) {
resultDiv.textContent = 'Invalid currency selected.';
return;
}
const fromRate = rates[fromCurrency];
const toRate = rates[toCurrency];
const convertedAmount = (amount / fromRate) * toRate;
resultDiv.textContent = `${amount} ${fromCurrency} = ${convertedAmount.toFixed(2)} ${toCurrency}`;
} catch (error) {
resultDiv.textContent = 'Error during conversion.';
console.error('Error during conversion:', error);
}
}
// Event listener for the convert button
convertButton.addEventListener('click', convertCurrency);
// Call the function to populate the currency options when the page loads
document.addEventListener('DOMContentLoaded', populateCurrencyOptions);
Let’s break down the code:
- Interfaces:
ExchangeRatesandApiResponsedefine the structure of the data we’ll be working with. This is a core TypeScript feature, ensuring type safety. - API_URL: This constant holds the API endpoint for fetching exchange rates. You’ll need to replace the example URL with a real API that provides currency exchange rates. Many free APIs are available, such as those from exchangerate-api.com or openexchangerates.org.
- DOM Element Selection: The code selects the HTML elements we’ll be interacting with (input fields, select boxes, the convert button, and the result div) using
document.getElementById(). Theas HTMLInputElement,as HTMLSelectElement, etc., parts are type assertions, telling TypeScript the expected type of the element. getExchangeRates()Function:- This asynchronous function fetches exchange rates from the API.
- It uses the
fetch()API to make a network request. - It handles potential errors (e.g., network issues, invalid API responses) using a
try...catchblock. - It parses the response as JSON using
response.json(). - It includes error handling to display an appropriate message to the user.
populateCurrencyOptions()Function:- This asynchronous function fetches the exchange rates and populates the currency select options.
- It extracts the currency codes from the API response.
- It dynamically creates
<option>elements for each currency and adds them to the select elements.
convertCurrency()Function:- This asynchronous function handles the currency conversion logic.
- It retrieves the amount, from currency, and to currency from the input fields.
- It performs basic input validation (checks for a valid amount).
- It fetches the exchange rates again (you could optimize this by caching).
- It retrieves the exchange rates for the selected currencies.
- It calculates the converted amount using the formula:
(amount / fromRate) * toRate. - It displays the result in the
resultDiv. - It also includes error handling.
- Event Listener: An event listener is attached to the convert button to trigger the
convertCurrency()function when the button is clicked. DOMContentLoadedEvent Listener: This event listener ensures that thepopulateCurrencyOptions()function is called after the HTML document has been fully loaded, so that the currency options are populated when the page loads.
Compiling and Running the Application
Now that we have our TypeScript code, let’s compile it and run the application.
1. Compile the TypeScript code: In your terminal, navigate to your project directory and run the following command:
tsc
This command will use the TypeScript compiler (tsc) to compile your index.ts file into a index.js file in the dist directory. The compiler will also check for type errors and report them.
2. Open the HTML file in your browser: Open the index.html file in your web browser. You should see the currency converter interface.
3. Test the application: Enter an amount, select currencies, and click the “Convert” button. The converted amount should be displayed below. If you encounter any errors, check the browser’s developer console (usually accessed by right-clicking on the page and selecting “Inspect” or “Inspect Element”) for error messages.
Common Mistakes and How to Fix Them
Here are some common mistakes and how to fix them:
- Incorrect API URL: Make sure the API URL is correct and that the API is working. Test the API URL in your browser to verify it returns the expected JSON data.
- CORS Errors: If you’re using a different API than the example, you might encounter CORS (Cross-Origin Resource Sharing) errors. This happens when the browser blocks requests to a different domain. To fix this, you might need to use a proxy server or configure CORS on the API server. Many free APIs will allow CORS requests from all origins.
- Type Errors: TypeScript will catch type errors during compilation. Review the error messages in your terminal and fix any type mismatches. Pay close attention to the interfaces you’ve defined and ensure that the data you’re receiving from the API matches those interfaces.
- Incorrect Element Selection: Double-check that you’re selecting the correct HTML elements using
document.getElementById(). Make sure the IDs in your JavaScript code match the IDs in your HTML. - Asynchronous Issues: Be careful with asynchronous operations (e.g., API calls). Make sure you’re using
async/awaitcorrectly and that you’re handling errors properly. Don’t try to access data that hasn’t finished loading yet. - Incorrect Calculations: Review the currency conversion formula to make sure it’s accurate. Also, be mindful of floating-point precision issues in JavaScript.
Key Takeaways
- TypeScript Fundamentals: You’ve learned how to use interfaces, types, and asynchronous functions.
- API Integration: You’ve successfully integrated with an external API to fetch real-time data.
- DOM Manipulation: You’ve learned how to manipulate the DOM to dynamically update the user interface.
- Error Handling: You’ve implemented error handling to make your application more robust.
- Web Development Workflow: You’ve gone through the process of setting up a project, writing code, compiling, and testing.
Enhancements and Next Steps
This is a basic currency converter. Here are some ideas for enhancements:
- Add more currencies: Expand the list of supported currencies.
- Implement currency symbols: Display currency symbols alongside the amounts.
- Add a date selector: Allow users to select a specific date to get historical exchange rates.
- Implement caching: Cache exchange rates to reduce API calls and improve performance.
- Add error handling UI: Display more informative error messages to the user.
- Add a loading indicator: Show a loading indicator while fetching exchange rates.
- Improve the UI: Use CSS frameworks like Bootstrap or Tailwind CSS to improve the look and feel.
- Add unit tests: Write unit tests to ensure the correctness of your code.
- Deploy the application: Deploy your application to a platform like Netlify or Vercel.
FAQ
Here are some frequently asked questions:
- What API should I use? There are many free and paid APIs available. Some popular options include exchangerate-api.com and openexchangerates.org. Make sure to check the API’s terms of service.
- How do I handle CORS errors? If you encounter CORS errors, you might need to use a proxy server or configure CORS on the API server. Many free APIs will allow CORS requests from all origins.
- Why is my application not displaying the correct exchange rates? Double-check the API URL and ensure that the API is returning the correct data. Also, verify that you’re correctly parsing the JSON response.
- How can I improve the performance of my currency converter? You can improve performance by caching exchange rates, using a more efficient API, and optimizing the UI.
- Where can I deploy my application? You can deploy your application to platforms like Netlify, Vercel, or GitHub Pages.
Building a currency converter is an excellent exercise in learning and applying TypeScript and web development principles. You’ve now built a functional application, and, more importantly, have the foundation for future projects. By understanding how to fetch data from APIs, manipulate the DOM, and handle user interactions, you’ve gained valuable skills that you can apply to a wide range of web development projects. The journey of software development is about continuous learning and improvement. Embrace the challenges, experiment with new features, and most importantly, keep building!
