A Practical Guide to TypeScript: How to Build a Pokedex App Using HTML, CSS, and TypeScript

TypeScript is a powerful superset of JavaScript that adds optional static typing, classes, and other features, with the goal of making JavaScript development more scalable and maintainable, especially for large codebases. Developed and maintained by Microsoft, TypeScript compiles to plain JavaScript, allowing it to run anywhere JavaScript runs – in browsers, on servers, on mobile devices, and more.

In this in-depth tutorial, we‘ll explore the core concepts and syntax of TypeScript and demonstrate how to use it to build a practical web application – a Pokedex app that fetches and displays data about Pokemon. We‘ll cover setting up a TypeScript development environment, configuring a project, and leveraging TypeScript‘s type system and object-oriented features to create a well-structured, maintainable codebase. Let‘s get started!

Setting Up the Development Environment

To get started with TypeScript, you‘ll need to have Node.js installed. You can then install the TypeScript compiler globally via npm:

npm install -g typescript

This will allow you to compile TypeScript code to JavaScript from any directory using the tsc command.

Next, create a new directory for the Pokedex project and initialize it as a TypeScript project:

mkdir pokedex-app
cd pokedex-app
tsc --init

The tsc --init command generates a tsconfig.json file with default TypeScript compiler options. Open this file and uncomment/modify any options as needed. A basic configuration may look like:

{
  "compilerOptions": {
    "target": "es6", 
    "module": "es6",
    "strict": true,
    "outDir": "./dist",  
    "rootDir": "./src",
  }
}

This specifies that the TypeScript code will be compiled to ES6-compliant JavaScript, the output will be placed in a dist directory, and the source TypeScript files are located in the src directory.

Creating the HTML and CSS

In the project root directory, create an index.html file with the following markup:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Pokedex App</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div id="app">

    <input type="text" id="search" placeholder="Search Pokemon">
    <div id="poke-container" class="poke-container"></div>
  </div>

  <script src="dist/index.js"></script>
</body>
</html>

And add some basic styling in a style.css file:

body {
  background: #efefbb;
  background: linear-gradient(to right, #d4d3dd, #efefbb);
  font-family: ‘Lato‘, sans-serif;
  margin: 0;
}

#app {
  display: flex;  
  flex-direction: column;
  align-items: center;
}

.poke-container {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  margin: 0 auto;
  max-width: 1200px;
}

.pokemon {
  background-color: #eee;
  border-radius: 20px;
  box-shadow: 0 3px 15px rgba(100, 100, 100, 0.5);
  margin: 10px;
  padding: 20px;
  text-align: center;
}

Fetching Data with TypeScript

Now let‘s dive into the TypeScript code to fetch and display the Pokemon data. In the src directory, create an index.ts file.

First, define an interface that describes the structure of a Pokemon object:

interface Pokemon {
  id: number;
  name: string;
  types: PokemonType[];
  sprite: string;
}

interface PokemonType {
  slot: number;
  type: {
    name: string;
    url: string;
  };
}

The Pokemon interface specifies that each Pokemon has an id (number), name (string), types (array of PokemonType), and sprite (image URL string).

Next, add a function to fetch Pokemon data from the PokeAPI:

const fetchPokemon = async (id: number): Promise<Pokemon> => {
  const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`);
  const data = await response.json();

  return {
    id: data.id,
    name: data.name,
    types: data.types,
    sprite: data.sprites.front_default
  };
}

This fetchPokemon function takes a Pokemon id number, makes a request to the PokeAPI endpoint for that specific Pokemon, and returns a Promise resolving to a Pokemon object.

Now let‘s create the function that will fetch multiple Pokemon and render them to the page:

const renderPokemon = (pokemon: Pokemon): string => {
  const types = pokemon.types.map(({type}) => type.name).join(‘, ‘);

  return `
    <div class="pokemon">
      <img src="${pokemon.sprite}" alt="${pokemon.name}" />
      <h3>${pokemon.name}</h3>
      <p>Types: ${types}</p>
    </div>
  `;
};

const displayPokemon = async () => {
  const container = document.getElementById(‘poke-container‘);

  // Fetch and display first 151 Pokemon
  for(let i = 1; i <= 151; i++) {
    const pokemon = await fetchPokemon(i);
    const pokemonHtml = renderPokemon(pokemon);

    container.innerHTML += pokemonHtml;
  }
}

displayPokemon();

The renderPokemon function takes a Pokemon object and returns an HTML string to render that Pokemon to the page. The displayPokemon function fetches the first 151 Pokemon by iterating from 1 to 151 and calling fetchPokemon for each id. It then renders each Pokemon to the page by calling renderPokemon and appending the resulting HTML to the #poke-container div.

Adding Search Functionality

Let‘s add the ability to search for Pokemon by name. Update the displayPokemon function to the following:

const displayPokemon = async (search: string = ‘‘) => {
  const container = document.getElementById(‘poke-container‘);
  container.innerHTML = ‘‘;

  for(let i = 1; i <= 151; i++) {
    const pokemon = await fetchPokemon(i);

    if(pokemon.name.includes(search.toLowerCase())) {
      const pokemonHtml = renderPokemon(pokemon);
      container.innerHTML += pokemonHtml;  
    }
  }
}

const handleSearch = () => {
  const searchInput = document.getElementById(‘search‘) as HTMLInputElement;

  searchInput.addEventListener(‘input‘, () => {
    displayPokemon(searchInput.value);
  });
};

displayPokemon();
handleSearch();

Now the displayPokemon function accepts an optional search string parameter. It clears the current contents of #poke-container, fetches all 151 Pokemon as before, but only renders ones whose name includes the search string (case-insensitive).

The new handleSearch function sets up an event listener on the search input, so that whenever its value changes, displayPokemon is called again with the new search value, reactive re-rendering the matching Pokemon.

Compiling to JavaScript

With the TypeScript code complete, it‘s time to compile it to plain JavaScript that can run in the browser. Simply run:

tsc

This will compile index.ts and output the resulting JavaScript to dist/index.js based on the configuration in tsconfig.json.

Now open index.html in a browser and you should see the Pokedex app fully functioning!

Conclusion

In this tutorial, we learned how to set up a TypeScript project, explored key TypeScript concepts like interfaces and typing, and used them to build a practical Pokedex web application that fetches and displays data from an external API.

TypeScript‘s static typing, interfaces, and rich tooling help create more maintainable and scalable JavaScript applications by catching type-related bugs early, providing better code documentation, and enabling powerful editor tooling and autocomplete.

To dive deeper into TypeScript, explore more of its features like classes, generics, and decorators, and consider using it in your next JavaScript project to leverage its power. Happy coding!

Additional Resources

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *