TypeScript Tutorial: Building a Simple Web Application for a Poll System

In today’s digital world, gathering opinions and feedback is crucial. Polls are a fantastic way to engage users, understand their preferences, and make data-driven decisions. Whether you’re a blogger wanting to know what your audience thinks, a marketer gauging product interest, or a developer looking to add interactivity to your website, a poll system can be a valuable asset. This tutorial will guide you through building a simple, yet functional, web application for a poll system using TypeScript, a superset of JavaScript that adds static typing.

Why TypeScript?

Before diving into the code, let’s discuss why TypeScript is a great choice for this project. TypeScript offers several benefits over plain JavaScript:

  • Improved Code Quality: Static typing helps catch errors early in the development process, reducing runtime bugs.
  • Enhanced Readability: Types make your code easier to understand and maintain.
  • Better Tooling: TypeScript provides excellent support for code completion, refactoring, and other IDE features.
  • Scalability: TypeScript makes it easier to manage larger codebases.

Project Setup

Let’s get started by setting up our project. You’ll need Node.js and npm (Node Package Manager) installed on your system. Open your terminal and create a new project directory:

mkdir poll-system-app
cd poll-system-app

Initialize a new npm project:

npm init -y

Now, install TypeScript and a few other necessary packages:

npm install typescript @types/node --save-dev
  • typescript: The TypeScript compiler.
  • @types/node: Type definitions for Node.js, allowing us to use Node.js modules in our TypeScript code.

Next, create a tsconfig.json file in the root directory. This file configures the TypeScript compiler:

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true, 
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"]
}

Here’s a breakdown of the key compiler options:

  • target: Specifies the JavaScript version to compile to (es6 is a good choice for modern browsers).
  • module: Specifies the module system (commonjs is used for Node.js).
  • outDir: The directory where compiled JavaScript files will be placed.
  • rootDir: The root directory of your TypeScript source files.
  • strict: Enables strict type-checking. Highly recommended.
  • esModuleInterop: Enables interoperability between CommonJS and ES modules.
  • skipLibCheck: Skips type checking of declaration files (improves build speed).
  • forceConsistentCasingInFileNames: Enforces consistent casing in file names.

Finally, create a src directory and a file named index.ts inside it. This is where we’ll write our TypeScript code.

Defining the Data Structures

Let’s define the data structures for our poll. We’ll need to represent a poll and its options. Create a file named src/types.ts and add the following code:

// src/types.ts

export interface PollOption {
  id: number;
  text: string;
  votes: number;
}

export interface Poll {
  id: number;
  question: string;
  options: PollOption[];
  totalVotes: number;
}

Here, we define two interfaces:

  • PollOption: Represents a single option in a poll. It has an id, a text description, and a votes count.
  • Poll: Represents the entire poll. It has an id, a question, an array of options (of type PollOption), and a totalVotes count.

Implementing the Poll Logic

Now, let’s write the core logic for our poll system in src/index.ts:

// src/index.ts
import { Poll, PollOption } from './types';

// Sample poll data (in a real app, this would come from a database)
let polls: Poll[] = [
  {
    id: 1,
    question: "What is your favorite programming language?",
    options: [
      { id: 1, text: "JavaScript", votes: 10 },
      { id: 2, text: "TypeScript", votes: 15 },
      { id: 3, text: "Python", votes: 8 },
    ],
    totalVotes: 33,
  },
];

// Function to display the poll
const displayPoll = (poll: Poll): void => {
  console.log(`Question: ${poll.question}`);
  poll.options.forEach((option) => {
    console.log(`  ${option.id}. ${option.text} (${option.votes} votes)`);
  });
  console.log(`Total votes: ${poll.totalVotes}`);
};

// Function to handle a vote
const castVote = (pollId: number, optionId: number): void => {
  const pollIndex = polls.findIndex((poll) => poll.id === pollId);

  if (pollIndex === -1) {
    console.log("Poll not found.");
    return;
  }

  const poll = polls[pollIndex];
  const optionIndex = poll.options.findIndex((option) => option.id === optionId);

  if (optionIndex === -1) {
    console.log("Option not found.");
    return;
  }

  poll.options[optionIndex].votes++;
  poll.totalVotes++;
  console.log("Vote cast successfully!");
};

// Example usage
const main = () => {
  // Display the first poll
  displayPoll(polls[0]);

  // Cast a vote for option 2 in poll 1
  castVote(1, 2);

  // Display the updated poll
  console.log("nUpdated poll:");
  displayPoll(polls[0]);
};

main();

Let’s break down this code:

  • Import Statements: We import the Poll and PollOption interfaces from ./types.
  • Sample Poll Data: We initialize an array called polls containing a sample poll. In a real-world application, you would fetch this data from a database.
  • displayPoll Function: This function takes a Poll object as input and displays the poll question, options, and vote counts to the console.
  • castVote Function: This function takes the pollId and optionId as input. It finds the poll and the selected option, increments the vote count for that option, and updates the total votes count. It also includes error handling to check if the poll or option exists.
  • main Function: This function demonstrates how to use the displayPoll and castVote functions. It displays the initial poll, casts a vote, and then displays the updated poll.

Running the Application

To run the application, we need to compile the TypeScript code and then execute the compiled JavaScript. Add a build script to your package.json file:

{
  "name": "poll-system-app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^20.11.16",
    "typescript": "^5.3.3"
  }
}

Now, in your terminal, run the following commands:

npm run build
npm start

This will compile your TypeScript code into JavaScript (in the dist directory) and then execute the compiled code using Node.js. You should see the poll displayed in the console, followed by the updated poll after the vote is cast.

Adding User Input (Optional)

To make our poll system more interactive, we can add user input. Here’s how to modify the src/index.ts to accept input from the console using the readline module:

// src/index.ts
import { Poll, PollOption } from './types';
import * as readline from 'readline';

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

// Sample poll data
let polls: Poll[] = [
  {
    id: 1,
    question: "What is your favorite programming language?",
    options: [
      { id: 1, text: "JavaScript", votes: 10 },
      { id: 2, text: "TypeScript", votes: 15 },
      { id: 3, text: "Python", votes: 8 },
    ],
    totalVotes: 33,
  },
];

const displayPoll = (poll: Poll): void => {
  console.log(`Question: ${poll.question}`);
  poll.options.forEach((option) => {
    console.log(`  ${option.id}. ${option.text} (${option.votes} votes)`);
  });
  console.log(`Total votes: ${poll.totalVotes}`);
};

const castVote = (pollId: number, optionId: number): void => {
  const pollIndex = polls.findIndex((poll) => poll.id === pollId);

  if (pollIndex === -1) {
    console.log("Poll not found.");
    return;
  }

  const poll = polls[pollIndex];
  const optionIndex = poll.options.findIndex((option) => option.id === optionId);

  if (optionIndex === -1) {
    console.log("Option not found.");
    return;
  }

  poll.options[optionIndex].votes++;
  poll.totalVotes++;
  console.log("Vote cast successfully!");
};

const getUserInput = (question: string): Promise => {
  return new Promise((resolve) => {
    rl.question(question, (answer) => {
      resolve(answer);
    });
  });
};

const main = async () => {
  displayPoll(polls[0]);

  const pollIdInput = await getUserInput("Enter the poll ID to vote in: ");
  const optionIdInput = await getUserInput("Enter the option ID to vote for: ");

  const pollId = parseInt(pollIdInput, 10);
  const optionId = parseInt(optionIdInput, 10);

  castVote(pollId, optionId);

  console.log("nUpdated poll:");
  displayPoll(polls[0]);

  rl.close();
};

main();

Key changes:

  • Import readline: We import the built-in readline module.
  • Create readline Interface: We create an interface to handle user input and output.
  • getUserInput Function: This asynchronous function prompts the user with a question and returns a promise that resolves with the user’s input.
  • Modified main Function: The main function now uses getUserInput to ask the user for the poll ID and option ID. It then casts the vote and displays the updated poll.
  • Close readline: We close the readline interface after the vote is cast.

To use this enhanced version, you’ll need to install the @types/node package if you haven’t already. Rebuild and run the application, and you’ll be prompted to enter the poll ID and option ID in the console.

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: TypeScript’s primary benefit is type checking. Don’t ignore the error messages! They guide you to fix potential bugs. Use your IDE’s features to quickly identify and fix them.
  • Not Using Strict Mode: Enable the strict option in your tsconfig.json file. This enables a suite of strict type-checking options that can help you catch more errors.
  • Mixing JavaScript and TypeScript: While TypeScript can interoperate with JavaScript, try to write as much of your code in TypeScript as possible to take full advantage of its features.
  • Incorrectly Typing Variables: Pay close attention to the types of your variables. Use type annotations (e.g., let myVariable: string = "hello";) to specify the expected types.
  • Forgetting to Compile: Remember to compile your TypeScript code before running it. Use the command npm run build.

Summary / Key Takeaways

In this tutorial, we’ve built a simple poll system using TypeScript. We’ve covered the basics of setting up a TypeScript project, defining data structures, implementing poll logic, and handling user input. You’ve learned how to use TypeScript to create a more robust and maintainable application. The use of static typing, clear code structure, and error handling are crucial for building reliable software. Remember to leverage TypeScript’s type system to catch errors early and improve the overall quality of your code. You can further expand on this by integrating a database, adding a user interface (using HTML, CSS, and JavaScript, or a framework like React or Vue.js), and implementing more advanced features like user authentication and real-time updates.

FAQ

Here are some frequently asked questions about building a poll system with TypeScript:

  1. Can I use a database to store the poll data? Absolutely! In a real-world application, you would typically store poll data in a database like PostgreSQL, MySQL, MongoDB, or others. You would modify the code to fetch data from and save data to the database.
  2. How can I add a user interface? You can use HTML, CSS, and JavaScript to build a user interface for your poll system. You can also use a front-end framework like React, Angular, or Vue.js to simplify the development process.
  3. How do I handle user authentication? You can implement user authentication using various methods, such as username/password login, OAuth (e.g., Google, Facebook), or other authentication providers. This involves storing user credentials securely and verifying them during the login process.
  4. How can I make the poll system real-time? You can use WebSockets or server-sent events (SSE) to enable real-time updates. This allows the poll results to update automatically as users vote, providing a more interactive experience.

Creating a functional poll system demonstrates the power of TypeScript in building interactive web applications. You now have a solid foundation for building more complex applications that gather and display information in real-time. This project is a starting point, and you can build upon it to create more feature-rich and engaging polls.