Managing finances can be a daunting task, and keeping track of expenses often feels like an uphill battle. Spreadsheets can become unwieldy, and existing apps might lack the flexibility to suit your unique needs. Wouldn’t it be great to have a personalized, easy-to-use tool to monitor your spending habits? This tutorial will guide you through building a simple, interactive web-based expense tracker using TypeScript. By the end of this guide, you’ll have a functional application and a solid understanding of fundamental TypeScript concepts.
Why Build an Expense Tracker?
An expense tracker provides several benefits:
- Improved Financial Awareness: Gain insights into where your money goes.
- Budgeting: Easily track spending against your budget.
- Goal Setting: Monitor progress towards financial goals.
- Customization: Tailor the app to your specific categories and needs.
This tutorial offers a practical project to learn TypeScript and apply those skills to solve a real-world problem. It’s a stepping stone to more complex applications, solidifying your understanding of the language.
Setting Up Your Development Environment
Before diving into the code, let’s set up the necessary tools:
- Node.js and npm: Install Node.js, which includes npm (Node Package Manager). You can download it from nodejs.org.
- Text Editor/IDE: Choose a code editor like Visual Studio Code (VS Code), Sublime Text, or WebStorm. VS Code is highly recommended due to its excellent TypeScript support.
- TypeScript Compiler: Install the TypeScript compiler globally using npm:
npm install -g typescript
Verify the installation by checking the version:
tsc -v
Project Structure
Create a new project directory and initialize it with npm:
mkdir expense-tracker
cd expense-tracker
npm init -y
This creates a package.json file. Next, create the following directory structure:
expense-tracker/
├── src/
│ ├── index.ts
│ └── styles.css
├── index.html
├── package.json
└── tsconfig.json
src/index.ts: The main TypeScript file where we’ll write our application logic.src/styles.css: CSS file for styling.index.html: The HTML file that hosts our application.tsconfig.json: TypeScript compiler configuration file.
Configuring TypeScript
Create a tsconfig.json file in the root directory. This file tells the TypeScript compiler how to compile your code. A basic configuration looks like this:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"]
}
target: "es5": Specifies the JavaScript version to compile to. You can use “es6” or “esnext” for modern browsers.module: "commonjs": Specifies the module system. Other options include “esnext”.outDir: "./dist": Where the compiled JavaScript files will be placed.strict: true: Enables strict type checking. Highly recommended for catching errors early.esModuleInterop: true: Enables interoperability between CommonJS and ES modules.skipLibCheck: true: Skips type checking of declaration files (.d.ts).forceConsistentCasingInFileNames: true: Enforces consistent casing in filenames.include: ["src/**/*"]: Specifies which files to include in the compilation.
Creating the HTML Structure
Create the index.html file with the basic structure:
<!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="src/styles.css">
</head>
<body>
<div id="app">
<h1>Expense Tracker</h1>
<div id="expense-form">
<label for="description">Description:</label>
<input type="text" id="description"><br>
<label for="amount">Amount:</label>
<input type="number" id="amount"><br>
<label for="category">Category:</label>
<select id="category">
<option value="food">Food</option>
<option value="transportation">Transportation</option&n <option value="housing">Housing</option>
<option value="utilities">Utilities</option>
<option value="other">Other</option>
</select><br>
<button id="add-expense">Add Expense</button>
</div>
<div id="expense-list">
<h2>Expenses</h2>
<ul id="expenses">
<!-- Expenses will be displayed here -->
</ul>
</div>
<div id="summary">
<h2>Summary</h2>
<p>Total Expenses: <span id="total-expenses">0</span></p>
</div>
</div>
<script src="dist/index.js"></script>
</body>
</html>
This HTML provides the basic structure: a title, an expense form with input fields for description, amount, and category, and an expense list to display the expenses.
Styling with CSS
Create a basic stylesheet in src/styles.css:
body {
font-family: sans-serif;
}
#app {
width: 80%;
margin: 20px auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
}
input[type="text"], input[type="number"], select {
width: 100%;
padding: 5px;
margin-bottom: 10px;
border: 1px solid #ccc;
border-radius: 3px;
}
button {
padding: 10px 15px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
}
#expenses li {
padding: 8px;
border-bottom: 1px solid #eee;
}
This CSS provides basic styling for the layout and elements.
Writing the TypeScript Logic
Now, let’s write the TypeScript code in src/index.ts. This is where the application logic resides.
// Define Expense interface
interface Expense {
description: string;
amount: number;
category: string;
}
// Get references to HTML elements
const descriptionInput = document.getElementById('description') as HTMLInputElement;
const amountInput = document.getElementById('amount') as HTMLInputElement;
const categorySelect = document.getElementById('category') as HTMLSelectElement;
const addExpenseButton = document.getElementById('add-expense') as HTMLButtonElement;
const expensesList = document.getElementById('expenses') as HTMLUListElement;
const totalExpensesSpan = document.getElementById('total-expenses') as HTMLSpanElement;
// Initialize expenses array and total
let expenses: Expense[] = [];
let totalExpenses: number = 0;
// Function to add an expense
function addExpense() {
const description = descriptionInput.value;
const amount = parseFloat(amountInput.value);
const category = categorySelect.value;
// Input validation
if (!description || isNaN(amount) || amount {
const listItem = document.createElement('li');
listItem.textContent = `${expense.description} - $${expense.amount.toFixed(2)} (${expense.category})`;
expensesList.appendChild(listItem);
});
}
// Function to update the total expenses
function updateTotalExpenses() {
totalExpensesSpan.textContent = totalExpenses.toFixed(2);
}
// Event listener for the add expense button
addExpenseButton.addEventListener('click', addExpense);
// Initial render
renderExpenses();
updateTotalExpenses();
Let’s break down the code:
- Expense Interface: Defines the structure of an expense object, including properties for description, amount, and category. This improves code readability and maintainability.
- HTML Element References: Retrieves references to the HTML elements using their IDs. The
as HTMLInputElement,as HTMLSelectElement, etc., are type assertions that tell TypeScript the specific type of the element. - Expenses Array and Total: Initializes an array to store expenses and a variable to track the total.
addExpense()Function:- Retrieves values from the input fields.
- Input Validation: Checks for empty descriptions, invalid amounts (NaN, or non-positive). This prevents unexpected behavior.
- Creates an
Expenseobject. - Adds the expense to the
expensesarray and updates thetotalExpenses. - Calls
renderExpenses()andupdateTotalExpenses()to update the UI. - Clears the input fields.
renderExpenses()Function:- Clears the existing expense list.
- Iterates through the
expensesarray. - Creates a list item (
<li>) for each expense. - Sets the text content of the list item to display the expense details.
- Appends the list item to the
expensesList.
updateTotalExpenses()Function: Updates thetotalExpensesSpanwith the calculated total.- Event Listener: Attaches an event listener to the “Add Expense” button, calling the
addExpense()function when clicked. - Initial Render: Calls
renderExpenses()andupdateTotalExpenses()to display any initial data (in this case, an empty list and a total of 0).
Compiling and Running the Application
To compile the TypeScript code, run the following command in your terminal:
tsc
This will create a dist folder containing the compiled index.js file. Now, open index.html in your web browser. You should see the expense tracker interface. Enter expense details and click “Add Expense” to test the application.
Common Mistakes and Solutions
Here are some common mistakes and how to avoid them:
- Incorrect Element References: Make sure your element IDs in the HTML match the IDs you’re using in your TypeScript code. Use the browser’s developer tools (right-click, “Inspect”) to verify element IDs.
- Type Errors: TypeScript will help you catch type errors during development. Pay attention to the TypeScript compiler’s output and fix any type mismatches. Use type assertions (e.g.,
as HTMLInputElement) carefully, and only when necessary. - Missing Event Listeners: Ensure your event listeners are correctly attached to the appropriate elements. Double-check that you’re using the correct event (e.g., “click”) and that the function name is correct.
- Incorrect Data Types: Be mindful of data types. For example, use
parseFloat()to convert the amount input (which is a string) to a number. - Not Clearing Input Fields: After adding an expense, remember to clear the input fields so the user can easily add another expense.
Enhancements and Further Development
This is a basic expense tracker. Here are some ideas to enhance it:
- Local Storage: Save expenses to local storage so they persist when the user closes the browser.
- Categorization: Implement more categories or allow users to add custom categories.
- Date Input: Add a date input for each expense.
- Sorting/Filtering: Allow users to sort and filter expenses by date, category, or amount.
- Budgeting Feature: Add a budgeting feature to set a budget and track spending against it.
- Chart Visualization: Use a charting library (like Chart.js) to visualize expense data.
- Responsive Design: Make the application responsive for different screen sizes.
Key Takeaways
This tutorial has covered the following key concepts:
- TypeScript Fundamentals: Interfaces, variables, functions, event listeners.
- HTML and CSS Integration: How to structure and style your web application.
- DOM Manipulation: How to interact with HTML elements using JavaScript.
- Basic Input Validation: How to validate user input.
- Project Structure: Organizing your code for better maintainability.
FAQ
- Why use TypeScript instead of JavaScript? TypeScript adds static typing, which helps catch errors early, improves code readability, and makes it easier to maintain larger projects. It also provides better tooling support (autocompletion, refactoring).
- How do I debug my TypeScript code? Use your browser’s developer tools. Set breakpoints in your compiled JavaScript code (in the “Sources” tab). You can also use
console.log()statements to print values to the console. - Can I use this expense tracker on my phone? Yes, if you open the
index.htmlfile in your mobile browser. For a better mobile experience, you should make the application responsive with CSS and consider using a framework like React or Angular for more complex UI interactions. - Where can I learn more about TypeScript? The official TypeScript documentation (typescriptlang.org/docs/) is an excellent resource. You can also find many online courses and tutorials on platforms like Udemy, Coursera, and freeCodeCamp.
- How do I deploy this application? You can deploy the application to a web hosting service like Netlify, Vercel, or GitHub Pages. You’ll need to build your project (using
tsc) and then upload theindex.html,dist/index.js, andsrc/styles.cssfiles.
Building this expense tracker provides a foundation for understanding web development with TypeScript. As you experiment with the enhancements, you’ll gain a deeper appreciation for the power and flexibility of TypeScript, solidifying your skills and preparing you for more complex projects.
