In today’s interconnected world, dealing with multiple currencies is a common occurrence. Whether you’re traveling, shopping online, or managing international finances, having a reliable currency converter at your fingertips is incredibly useful. This tutorial will guide you through building a simple, interactive currency converter using TypeScript. We’ll explore the core concepts of TypeScript, learn how to fetch real-time exchange rates, and create a user-friendly interface. By the end, you’ll have a functional application and a solid understanding of how TypeScript can be used to create practical and engaging web applications.
Why TypeScript?
Before we dive into the code, let’s briefly discuss why TypeScript is an excellent choice for this project. TypeScript is a superset of JavaScript that adds static typing. This means you can define the data types of your variables, function parameters, and return values. This offers several benefits:
- Early Error Detection: TypeScript helps catch errors during development, before you even run your code. This can save you a lot of time and frustration.
- Improved Code Readability: Types make your code easier to understand and maintain.
- Enhanced Code Completion: IDEs (like VS Code) can provide better code completion and suggestions.
- Refactoring Safety: TypeScript makes it easier and safer to refactor your code.
In essence, TypeScript helps you write more robust, maintainable, and scalable code. It’s a great choice for projects of any size, including our currency converter.
Setting Up Your Project
Let’s get started by setting up our development environment. You’ll need:
- Node.js and npm (or yarn): These are essential for managing your project dependencies and running TypeScript.
- A Text Editor or IDE: VS Code is a popular choice, and it has excellent TypeScript support.
First, create a new project directory and navigate into it using your terminal:
mkdir currency-converter
cd currency-converter
Next, initialize a new npm project:
npm init -y
This will create a `package.json` file in your project directory. Now, install TypeScript and a few other necessary packages:
npm install typescript @types/node --save-dev
We’ve installed:
- typescript: The TypeScript compiler.
- @types/node: Type definitions for Node.js (needed if you’re using Node.js modules).
Next, create a `tsconfig.json` file. This file configures the TypeScript compiler. You can generate a basic one using the following command:
npx tsc --init
This will create a `tsconfig.json` file in your project. You can customize this file to adjust how TypeScript compiles your code. For this tutorial, we’ll keep the default settings, which are generally suitable for a beginner project. However, you might want to modify the `outDir` option to specify where the compiled JavaScript files should be placed.
Building the Core Logic: Fetching Exchange Rates
The heart of our currency converter is fetching the latest exchange rates. We’ll use a free API for this purpose. There are many free APIs available; for this example, we’ll use a hypothetical API endpoint: `https://api.example.com/currency/rates`. This API is assumed to return a JSON response similar to this:
{
"base": "USD",
"rates": {
"EUR": 0.85,
"GBP": 0.73,
"JPY": 110.25
// ... more currencies
},
"date": "2024-01-01"
}
Let’s create a TypeScript file called `converter.ts` and write the code to fetch these rates.
// converter.ts
interface ExchangeRates {
[currency: string]: number;
}
interface ApiResponse {
base: string;
rates: ExchangeRates;
date: string;
}
async function getExchangeRates(baseCurrency: string): Promise<ApiResponse | null> {
const apiUrl = `https://api.example.com/currency/rates?base=${baseCurrency}`;
try {
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data: ApiResponse = await response.json();
return data;
} catch (error) {
console.error("Error fetching exchange rates:", error);
return null;
}
}
async function convertCurrency(amount: number, fromCurrency: string, toCurrency: string): Promise<number | null> {
const rates = await getExchangeRates(fromCurrency);
if (!rates) {
console.error("Failed to fetch exchange rates.");
return null;
}
if (!rates.rates[toCurrency]) {
console.error(`Exchange rate for ${toCurrency} not found.`);
return null;
}
const convertedAmount = amount * rates.rates[toCurrency];
return convertedAmount;
}
// Example usage (for testing in Node.js)
async function main() {
const amount = 100;
const fromCurrency = "USD";
const toCurrency = "EUR";
const convertedAmount = await convertCurrency(amount, fromCurrency, toCurrency);
if (convertedAmount !== null) {
console.log(`${amount} ${fromCurrency} is equal to ${convertedAmount.toFixed(2)} ${toCurrency}`);
}
}
main();
Let’s break down this code:
- Interfaces: We define two interfaces, `ExchangeRates` and `ApiResponse`, to strongly type our data and improve code readability. This is a core benefit of using TypeScript.
- `getExchangeRates()`: This asynchronous function fetches the exchange rates from the API. It handles potential errors, such as network issues or invalid responses.
- `convertCurrency()`: This function takes the amount, the source currency, and the target currency as input. It calls `getExchangeRates()` to retrieve the rates, then calculates the converted amount. It also includes error handling for cases where the API call fails or the target currency is not found.
- Error Handling: The code includes robust error handling using `try…catch` blocks and checks for API response status. This ensures that the application behaves gracefully even in error scenarios.
- Asynchronous Operations: The use of `async` and `await` makes the code cleaner and easier to read, especially when dealing with asynchronous operations like API calls.
- Example Usage: The `main()` function demonstrates how to use the `convertCurrency()` function, and the `toFixed(2)` method is used to format the output to two decimal places.
Building the User Interface (UI) with HTML and JavaScript
Now, let’s create a simple HTML and JavaScript UI for our currency converter. We’ll keep it straightforward for this tutorial. Create an `index.html` file in your project directory:
<!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>
<style>
body {
font-family: sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f4f4f4;
}
.converter-container {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 300px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="number"], select {
width: 100%;
padding: 8px;
margin-bottom: 10px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
button {
background-color: #4CAF50;
color: white;
padding: 10px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
width: 100%;
}
button:hover {
background-color: #3e8e41;
}
#result {
margin-top: 10px;
font-weight: bold;
}
</style>
</head>
<body>
<div class="converter-container">
<h2>Currency Converter</h2>
<label for="amount">Amount:</label>
<input type="number" id="amount" placeholder="Enter amount">
<label for="fromCurrency">From:</label>
<select id="fromCurrency">
<option value="USD">USD</option>
<option value="EUR">EUR</option>
<option value="GBP">GBP</option>
<option value="JPY">JPY</option>
</select>
<label for="toCurrency">To:</label>
<select id="toCurrency">
<option value="EUR">EUR</option>
<option value="USD">USD</option>
<option value="GBP">GBP</option>
<option value="JPY">JPY</option>
</select>
<button id="convertButton">Convert</button>
<div id="result"></div>
</div>
<script src="converter.js"></script>
</body>
</html>
This HTML creates a basic form with:
- An input field for the amount.
- Two select dropdowns for the currencies (from and to).
- A button to trigger the conversion.
- A div to display the result.
Now, let’s create the `converter.js` file. This is where we’ll write the JavaScript code to handle the user interactions and call our TypeScript functions. We need to compile the TypeScript code into JavaScript first. Open your terminal, navigate to your project directory, and run:
tsc
This will compile your `converter.ts` file into `converter.js`. Now, create a `converter.js` file and add the following JavaScript code. Note: This code assumes that the `converter.ts` file has been compiled into `converter.js`.
// converter.js (This file is generated by the TypeScript compiler)
async function getExchangeRates(baseCurrency) {
const apiUrl = `https://api.example.com/currency/rates?base=${baseCurrency}`;
try {
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error("Error fetching exchange rates:", error);
return null;
}
}
async function convertCurrency(amount, fromCurrency, toCurrency) {
const rates = await getExchangeRates(fromCurrency);
if (!rates) {
console.error("Failed to fetch exchange rates.");
return null;
}
if (!rates.rates[toCurrency]) {
console.error(`Exchange rate for ${toCurrency} not found.`);
return null;
}
const convertedAmount = amount * rates.rates[toCurrency];
return convertedAmount;
}
// UI Logic
const amountInput = document.getElementById('amount');
const fromCurrencySelect = document.getElementById('fromCurrency');
const toCurrencySelect = document.getElementById('toCurrency');
const convertButton = document.getElementById('convertButton');
const resultDiv = document.getElementById('result');
convertButton.addEventListener('click', async () => {
const amount = parseFloat(amountInput.value);
const fromCurrency = fromCurrencySelect.value;
const toCurrency = toCurrencySelect.value;
if (isNaN(amount)) {
resultDiv.textContent = 'Please enter a valid number.';
return;
}
const convertedAmount = await convertCurrency(amount, fromCurrency, toCurrency);
if (convertedAmount !== null) {
resultDiv.textContent = `${amount} ${fromCurrency} is equal to ${convertedAmount.toFixed(2)} ${toCurrency}`;
} else {
resultDiv.textContent = 'Conversion failed. Check the currencies and try again.';
}
});
Let’s break down the JavaScript code:
- UI Element Selection: The code selects the necessary HTML elements (input, selects, button, and result div) using their IDs.
- Event Listener: An event listener is added to the convert button. When the button is clicked, the code retrieves the amount and currencies from the input and select elements.
- Input Validation: The code checks if the entered amount is a valid number.
- Calling `convertCurrency()`: The code calls the `convertCurrency()` function (imported from the compiled TypeScript) to perform the conversion.
- Displaying the Result: The converted amount is displayed in the result div, or an error message is shown if the conversion fails.
To run this, open `index.html` in your web browser. You should see the currency converter interface. Enter the amount, select the currencies, and click the convert button to see the result. You may need to adjust the API endpoint to a valid currency exchange rate provider.
Handling Errors and Edge Cases
While the code includes basic error handling, we can improve it further. Consider these scenarios and how to address them:
- Invalid API Responses: If the API returns an unexpected format, our code could crash. Implement more robust error handling in the `getExchangeRates()` function to handle different response structures.
- Network Errors: The API call might fail due to network issues. The current code handles this, but you could add more user-friendly error messages.
- Rate Limiting: Some APIs have rate limits. Implement logic to handle these limits (e.g., display a message to the user if the rate limit is exceeded).
- User Input Validation: Add more validation to the user input to prevent unexpected behavior (e.g., prevent the user from entering negative amounts).
Here’s an example of how you can add more robust error handling to the `getExchangeRates()` function:
async function getExchangeRates(baseCurrency: string): Promise<ApiResponse | null> {
const apiUrl = `https://api.example.com/currency/rates?base=${baseCurrency}`;
try {
const response = await fetch(apiUrl);
if (!response.ok) {
console.error(`HTTP error! Status: ${response.status}`);
// Additional error handling based on status code
if (response.status === 429) {
// Handle rate limiting
console.error("Rate limit exceeded. Please try again later.");
}
return null;
}
const data: ApiResponse = await response.json();
// Validate the API response structure
if (!data || !data.base || !data.rates || typeof data.base !== 'string' || typeof data.rates !== 'object') {
console.error("Invalid API response format.");
return null;
}
return data;
} catch (error) {
console.error("Error fetching exchange rates:", error);
return null;
}
}
Testing Your Application
Testing is crucial to ensure that your currency converter works as expected. Here are some testing strategies:
- Unit Tests: Test individual functions (e.g., `getExchangeRates()`, `convertCurrency()`) in isolation. You can use a testing framework like Jest or Mocha for this.
- Integration Tests: Test the interaction between different parts of your application (e.g., the UI and the API calls).
- End-to-End Tests: Simulate user interactions to test the entire application flow.
- Manual Testing: Manually test the application with different inputs and scenarios to ensure that it behaves correctly.
Here’s a basic example of a unit test for the `convertCurrency` function using Jest:
// converter.test.js (Requires Jest to be installed)
const { convertCurrency } = require('./converter'); // Assuming your compiled JS is in the same directory
// Mock the getExchangeRates function to avoid making actual API calls during testing
jest.mock('./converter', () => ({
...jest.requireActual('./converter'), // Import the original module to maintain other functions
getExchangeRates: jest.fn().mockResolvedValue({
base: 'USD',
rates: {
EUR: 0.85,
},
date: '2024-01-01',
}),
}));
test('convertCurrency should return the correct converted amount', async () => {
const amount = 100;
const fromCurrency = 'USD';
const toCurrency = 'EUR';
const convertedAmount = await convertCurrency(amount, fromCurrency, toCurrency);
expect(convertedAmount).toBe(85);
});
test('convertCurrency should return null if the rate is not found', async () => {
const amount = 100;
const fromCurrency = 'USD';
const toCurrency = 'JPY'; // Rate not mocked
const convertedAmount = await convertCurrency(amount, fromCurrency, toCurrency);
expect(convertedAmount).toBeNull();
});
Deployment
Once you’ve built and tested your currency converter, you might want to deploy it so others can use it. Here are some deployment options:
- Static Site Hosting: Services like Netlify, Vercel, or GitHub Pages are great for hosting static HTML, CSS, and JavaScript applications.
- Web Server: You can deploy your application on a web server like Apache or Nginx.
- Cloud Platforms: Platforms like AWS, Google Cloud, or Azure offer various deployment options.
To deploy using a static site hoster, you typically:
- Compile your TypeScript code to JavaScript using `tsc`.
- Upload your `index.html`, `converter.js`, and any other assets (CSS, images) to the hosting platform.
The specific steps will vary depending on the platform you choose.
Common Mistakes and How to Fix Them
Here are some common mistakes and how to avoid them:
- Incorrect TypeScript Setup: Ensure your `tsconfig.json` is configured correctly, and that you have installed the necessary dependencies (TypeScript, Node.js types). Double-check the path for your compiled JavaScript file in your HTML file.
- Typing Errors: TypeScript will help you catch many typing errors, but make sure you are using types consistently. Review the TypeScript compiler’s output and fix any type-related errors.
- Asynchronous Issues: When working with asynchronous operations (like `fetch`), make sure you are using `async/await` correctly or using `.then()` and `.catch()` appropriately.
- Incorrect API Endpoint: Ensure your API endpoint is correct and that the API you are using is functional. Test the API endpoint directly in your browser or with a tool like Postman to verify that it’s returning the expected data.
- CORS Errors: If you’re encountering CORS (Cross-Origin Resource Sharing) errors, it means your browser is blocking the request to the API. This usually happens because the API server doesn’t allow requests from your domain. If you control the API, you’ll need to configure it to allow requests from your domain. If you don’t control the API, you might need to use a proxy server.
Key Takeaways
- TypeScript Benefits: TypeScript enhances code quality, readability, and maintainability through static typing.
- API Integration: You learned how to fetch and process data from an external API.
- UI Development: You created a basic user interface with HTML, CSS, and JavaScript to interact with your application.
- Error Handling: You implemented error handling to make your application more robust.
- Testing: You learned about different testing strategies to ensure your application works as expected.
Frequently Asked Questions (FAQ)
- Why use TypeScript instead of JavaScript? TypeScript adds static typing to JavaScript, which helps catch errors early, improves code readability, and makes it easier to maintain and refactor your code.
- How do I find a free currency exchange rate API? There are many free APIs available. Search online for “free currency exchange API” to find options. Be sure to check the API’s terms of service.
- How do I deploy my currency converter? You can deploy it using a static site hosting service like Netlify or Vercel, or on a web server.
- What are some common TypeScript errors? Common errors include type mismatches, incorrect function signatures, and issues with asynchronous operations. The TypeScript compiler will help you identify these errors.
Building a currency converter is a great way to learn about TypeScript, API integration, and front-end development. By following this tutorial, you’ve gained practical experience in these areas. Remember that the code can be further extended to include more advanced features, such as historical exchange rates, currency charts, and user preferences. The concepts covered here provide a solid foundation for your future TypeScript projects. You can now adapt and expand this project to build other useful applications. The journey of learning never truly ends; embrace the process, keep experimenting, and enjoy the satisfaction of creating something functional and useful.
