Managing personal finances can be a daunting task, but it doesn’t have to be. In today’s digital age, numerous tools exist to help us track our income and expenses efficiently. In this tutorial, we’ll dive into the world of TypeScript and build a simple, interactive web-based expense tracker. This project will not only introduce you to the fundamentals of TypeScript but also provide a practical application for managing your finances, helping you visualize your spending habits and make informed decisions.
Why TypeScript?
Before we begin, let’s address the elephant in the room: why TypeScript? TypeScript is a superset of JavaScript that adds static typing. This means you can define the types of variables, function parameters, and return values. This feature offers several advantages:
- Early Error Detection: TypeScript catches potential errors during development, before you even run your code. This saves you time and frustration by preventing runtime surprises.
- Improved Code Readability: Type annotations make your code easier to understand and maintain, especially in larger projects.
- Enhanced Code Completion: IDEs can provide better code suggestions and autocompletion, increasing your productivity.
- Refactoring Safety: When you refactor your code, TypeScript helps ensure that your changes don’t introduce unexpected bugs by checking type compatibility.
In essence, TypeScript helps you write more robust, maintainable, and scalable JavaScript applications. It’s an excellent choice for any project where code quality and long-term maintainability are important, making it perfect for our expense tracker.
Project Overview
Our expense tracker will allow users to:
- Add new expenses with a description, amount, and category.
- View a list of expenses.
- Calculate the total expenses.
- Categorize expenses for better insights.
We’ll use HTML for the structure, CSS for styling, and TypeScript for the logic. We will not use any external frameworks or libraries to keep it simple and focused on the core concepts of TypeScript.
Setting Up the Project
Let’s get started by setting up our project environment:
- Create a Project Folder: Create a new folder for your project, e.g., “expense-tracker”.
- Initialize npm: Open your terminal, navigate to your project folder, and run
npm init -y. This will create a `package.json` file. - Install TypeScript: Install TypeScript globally or locally. For a local installation, run
npm install typescript --save-dev. - Create `tsconfig.json`: Create a `tsconfig.json` file in your project root. This file configures the TypeScript compiler. You can generate a basic one by running
npx tsc --initin your terminal. - Create HTML, CSS, and TS files: Create the following files in your project directory:
index.html: The main HTML file.style.css: The CSS file for styling.script.ts: The TypeScript file for the application logic.
Your project structure should look something like this:
expense-tracker/
├── index.html
├── package.json
├── script.ts
├── style.css
└── tsconfig.json
Writing the HTML
Let’s start by creating the basic HTML structure 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>Expense Tracker</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Expense Tracker</h1>
<div class="container">
<div class="form-container">
<h2>Add Expense</h2>
<form id="expense-form">
<div class="form-group">
<label for="description">Description:</label>
<input type="text" id="description" required>
</div>
<div class="form-group">
<label for="amount">Amount:</label>
<input type="number" id="amount" required>
</div>
<div class="form-group">
<label for="category">Category:</label>
<select id="category" required>
<option value="" disabled selected>Select Category</option>
<option value="food">Food</option>
<option value="transportation">Transportation</option>
<option value="housing">Housing</option>
<option value="utilities">Utilities</option>
<option value="other">Other</option>
</select>
</div>
<button type="submit">Add Expense</button>
</form>
</div>
<div class="expense-list-container">
<h2>Expenses</h2>
<ul id="expense-list">
</ul>
<p id="total-expenses">Total: $0.00</p>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
This HTML provides the basic structure for our expense tracker. It includes:
- A heading.
- A form for adding new expenses, including input fields for description, amount, and category.
- An unordered list to display the expenses.
- A paragraph to display the total expenses.
Styling with CSS
Let’s add some basic styling to `style.css` to make our application visually appealing:
body {
font-family: sans-serif;
margin: 0;
padding: 20px;
background-color: #f4f4f4;
}
h1 {
text-align: center;
color: #333;
}
.container {
display: flex;
justify-content: space-around;
margin-top: 20px;
}
.form-container, .expense-list-container {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 45%;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input[type="text"], 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: #45a049;
}
ul {
list-style: none;
padding: 0;
}
li {
padding: 10px;
margin-bottom: 5px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: #f9f9f9;
}
#total-expenses {
text-align: right;
font-weight: bold;
margin-top: 15px;
}
This CSS provides basic styling for the layout, form elements, and expense list.
Writing the TypeScript Logic
Now, let’s write the core logic in `script.ts`. This is where we’ll leverage the power of TypeScript.
First, we’ll define some types to represent our data:
// Define an Expense type
interface Expense {
description: string;
amount: number;
category: string;
}
This `Expense` interface defines the structure of our expense objects. Next, let’s get references to the HTML elements and add event listeners:
// Get references to HTML elements
const expenseForm = document.getElementById('expense-form') as HTMLFormElement;
const descriptionInput = document.getElementById('description') as HTMLInputElement;
const amountInput = document.getElementById('amount') as HTMLInputElement;
const categorySelect = document.getElementById('category') as HTMLSelectElement;
const expenseList = document.getElementById('expense-list') as HTMLUListElement;
const totalExpenses = document.getElementById('total-expenses') as HTMLParagraphElement;
// Initialize expenses array
let expenses: Expense[] = [];
// Function to render expenses
function renderExpenses() {
expenseList.innerHTML = ''; // Clear the list
let total = 0;
expenses.forEach(expense => {
const listItem = document.createElement('li');
listItem.textContent = `${expense.description} - $${expense.amount.toFixed(2)} (${expense.category})`;
expenseList.appendChild(listItem);
total += expense.amount;
});
totalExpenses.textContent = `Total: $${total.toFixed(2)}`;
}
// Function to add a new expense
function addExpense(expense: Expense) {
expenses.push(expense);
renderExpenses();
}
// Event listener for form submission
expenseForm.addEventListener('submit', (event: Event) => {
event.preventDefault(); // Prevent the default form submission
// Get values from the form
const description = descriptionInput.value;
const amount = parseFloat(amountInput.value);
const category = categorySelect.value;
// Validate inputs
if (!description || isNaN(amount) || amount <= 0 || category === '') {
alert('Please enter valid expense details.');
return;
}
// Create a new expense object
const newExpense: Expense = {
description: description,
amount: amount,
category: category,
};
// Add the expense
addExpense(newExpense);
// Clear the form
descriptionInput.value = '';
amountInput.value = '';
categorySelect.value = '';
});
// Initial render
renderExpenses();
Let’s break down the TypeScript code:
- Types: We define an `Expense` interface to ensure that our expense objects have the expected properties (description, amount, and category).
- Element References: We get references to the HTML elements we need to interact with, using type assertions (e.g., `as HTMLFormElement`) to provide type safety.
- Expenses Array: We initialize an array called `expenses` of type `Expense[]` to store our expense data.
- `renderExpenses()` Function: This function is responsible for rendering the expenses in the list. It clears the existing list, iterates through the `expenses` array, creates list items for each expense, and updates the total.
- `addExpense()` Function: This function adds a new expense to the `expenses` array and calls `renderExpenses()` to update the display.
- Event Listener: We add an event listener to the form’s `submit` event. When the form is submitted:
- `event.preventDefault()` prevents the default form submission behavior (which would refresh the page).
- We retrieve the values from the input fields.
- We validate the inputs to ensure they are valid.
- We create a new `Expense` object.
- We call `addExpense()` to add the new expense.
- We clear the form fields for the next entry.
- Initial Render: We call `renderExpenses()` at the end to display any initial expenses (if any) or an empty list.
Compiling and Running the Code
To compile your TypeScript code, run the following command in your terminal:
tsc script.ts
This command will compile `script.ts` into `script.js`. If you have configured your `tsconfig.json` correctly, you can also run `tsc` without specifying the file, which will compile all TypeScript files in your project. Make sure you have a script tag in your `index.html` file that references the generated `script.js` file:
<script src="script.js"></script>
Open `index.html` in your web browser. You should see your expense tracker application. You can now add expenses, and they should appear in the list with the total updated accordingly.
Common Mistakes and How to Fix Them
Here are some common mistakes beginners make when working with TypeScript and how to avoid them:
- Ignoring Type Errors: One of the biggest benefits of TypeScript is catching errors early. Don’t ignore the type errors that the compiler throws. Read the error messages carefully, understand what’s wrong, and fix your code accordingly. Your IDE will often highlight these errors in red.
- Incorrect Type Annotations: Make sure you’re using the correct types for your variables and function parameters. For example, use `number` for numbers, `string` for text, `boolean` for true/false values, and custom interfaces or types for complex data structures.
- Not Using Interfaces/Types: Define interfaces or types to represent the shape of your data. This makes your code more organized and easier to understand.
- Mixing JavaScript and TypeScript: While you can gradually introduce TypeScript into a JavaScript project, it’s best to write your new code entirely in TypeScript. Avoid mixing JavaScript and TypeScript in the same file unless absolutely necessary.
- Forgetting to Compile: Make sure you compile your TypeScript code (`tsc script.ts` or `tsc`) before running your application. If you don’t, your changes won’t be reflected in the browser.
- Incorrect File Paths: Double-check the file paths in your HTML and TypeScript files. A common mistake is using the wrong path when referencing the compiled JavaScript file in your HTML.
Advanced Features (Optional)
Once you’ve mastered the basics, you can enhance your expense tracker with more advanced features:
- Data Persistence: Use `localStorage` to save the expenses in the browser so that they persist even after the user closes the browser.
- Categorization: Add categories to your expenses and filter the list by category.
- Data Visualization: Use a charting library (like Chart.js) to visualize your spending habits with charts and graphs.
- Date Picker: Include a date picker to associate expenses with specific dates.
- Import/Export: Allow users to import and export their expense data (e.g., as a CSV file).
- More Categories: Add more categories in the category select, and allow the user to manage them, with a custom category option.
Key Takeaways
- TypeScript adds static typing to JavaScript, improving code quality and maintainability.
- Interfaces define the structure of your data, making your code more organized.
- Type annotations help catch errors early and improve readability.
- Event listeners allow your application to respond to user interactions.
- You can extend the functionality of the expense tracker with more advanced features.
FAQ
Here are some frequently asked questions about this project:
- Can I use a framework like React or Angular with this project? Yes, you can. However, this tutorial focuses on the core TypeScript concepts without any frameworks to keep things simple.
- How do I debug my TypeScript code? You can debug your TypeScript code using your browser’s developer tools. The browser will execute the compiled JavaScript code. You can set breakpoints in your `script.js` file (or the original `script.ts` file if your browser supports source maps) to step through your code and inspect variables.
- What if I get a “Cannot find name” error? This error usually means that the TypeScript compiler can’t find a variable, function, or type that you’re using. Double-check that you’ve declared the variable, function, or type correctly and that you’ve imported it if necessary.
- How can I deploy this application? You can deploy your application to a web hosting service, such as Netlify, Vercel, or GitHub Pages. These services typically allow you to deploy static websites directly from a Git repository.
We’ve created a functional, albeit simple, expense tracker with the power of TypeScript. You’ve learned how to set up a TypeScript project, define types, handle user input, and display data. The journey doesn’t end here, as you can continuously expand your knowledge and skills by exploring more advanced features and libraries. Embrace the challenge, experiment with different approaches, and you’ll find yourself creating more complex and sophisticated applications with TypeScript. The ability to manage your personal finances with a custom tool is a rewarding experience, and this tracker is a great starting point for your development projects. With a little extra effort, you can transform this basic tool into a powerful application tailored to your specific needs.
