Share
Explore

TypeScript Pokemon Application

megaphone

Here is how we will proceed:

Use VSC to make this code base and compile it with TypeScript:
Start by making a directory for your Project.
image.png

megaphone

After you have completed all the sections below: Implement these instructions to compile and run your TypeScript

application:

Lab Instruction: Compiling and Running TypeScript Code

Install TypeScript: If you haven't already, install TypeScript globally on your system using npm or yarn:
npm install -g typescript
or
yarn global add typescript
Navigate to Project Directory: Open your terminal or command prompt and navigate to the root directory of your Pokémon Gym project.
Create tsconfig.json: Create a tsconfig.json file in the project root. This file configures the TypeScript compiler. You can use the following basic configuration:
JSON{ "compilerOptions": { "target": "es6", "module": "commonjs", "outDir": "./dist", "strict": true } }
Compile the Code: Run the TypeScript compiler using the following command in your terminal:
tsc
This will compile your TypeScript code into JavaScript files and place them in the dist directory as specified in your tsconfig.json.
Run the Application: After compilation, you can run your application using Node.js. Assuming your main application file is dist/index.js, you would run:
Bashnode dist/index.js
This will execute your compiled JavaScript code and you should see the output of your Pokémon Gym application in the terminal.

I will outline a TypeScript-based application that models a Pokémon Gym business domain.
This application will include data structures to manage information on Pokémon battles, evolution stages, and trainer ownership using JSON-based data storage, serving as a precursor to a future MongoDB implementation.
I will also suggest design patterns conducive to Agile traceability and CI/CD pipeline integration, especially given that automated testing with JEST and GitHub Actions will be central to the process.


1. Project Structure pokemon-gym/ ├── src/ │ ├── index.ts // Main application file: controller, starts the application and runs everything │ ├── models/ │ │ ├── Pokemon.ts // Pokemon model │ │ ├── Trainer.ts // Trainer model │ │ └── Battle.ts // Battle model │ ├── data/ │ │ └── pokedex.json // JSON Pokedex data │ │ └── trainers.json // JSON Trainer data │ ├── /services │ │ ├── PokedexService.ts // Service to manage Pokemon data │ │ └── TrainerService.ts // Service to manage Trainer data │ └── utils/ │ └── types.ts // Shared type definitions ├── tests/ // Jest test files │ ├── Pokemon.test.ts // Pokemon tests │ └── Trainer.test.ts // Trainer tests ├── jest.config.ts // Jest configuration └── package.json ```

2. Model Definitions

`Pokemon.ts`
// src/models/Pokemon.ts export interface Pokemon { id: number; name: string; type: string[]; level: number; evolved: boolean; evolutions: string[]; battleStats: BattleStats; }
export interface BattleStats { wins: number; losses: number; draws: number; }
export class Pokemon implements Pokemon { constructor( public id: number, public name: string, public type: string[], public level: number, public evolved: boolean, public evolutions: string[], public battleStats: BattleStats = { wins: 0, losses: 0, draws: 0 } ) {}
levelUp(): void { this.level++; }
evolve(): boolean { if (!this.evolved && this.evolutions.length > 0) { this.evolved = true; return true; } return false; }
recordBattleResult(result: 'win' | 'loss' | 'draw'): void { if (result === 'win') this.battleStats.wins++; if (result === 'loss') this.battleStats.losses++; if (result === 'draw') this.battleStats.draws++; } } ```
Trainer.ts`
// src/models/Trainer.ts import { Pokemon } from './Pokemon';
export class Trainer { constructor( public id: number, public name: string, public pokemons: Pokemon[] = [] ) {}
addPokemon(pokemon: Pokemon): void { this.pokemons.push(pokemon); }
getPokemon(id: number): Pokemon | undefined { return this.pokemons.find(pokemon => pokemon.id === id); } }
`Battle.ts`
// src/models/Battle.ts import { Pokemon } from './Pokemon';
export interface BattleResult { winner: Pokemon | null; loser: Pokemon | null; isDraw: boolean; }
export class Battle { static conductBattle(pokemon1: Pokemon, pokemon2: Pokemon): BattleResult { // Simple battle logic based on level (could be extended) if (pokemon1.level > pokemon2.level) { pokemon1.recordBattleResult('win'); pokemon2.recordBattleResult('loss'); return { winner: pokemon1, loser: pokemon2, isDraw: false }; } else if (pokemon1.level < pokemon2.level) { pokemon2.recordBattleResult('win'); pokemon1.recordBattleResult('loss'); return { winner: pokemon2, loser: pokemon1, isDraw: false }; } else { pokemon1.recordBattleResult('draw'); pokemon2.recordBattleResult('draw'); return { winner: null, loser: null, isDraw: true }; } } }

3. Services

#### `PokedexService.ts`
```typescript // src/services/PokedexService.ts import { Pokemon } from '../models/Pokemon'; import * as pokedexData from '../data/pokedex.json';
export class PokedexService { private pokedex: Pokemon[] = pokedexData;
getPokemonById(id: number): Pokemon | undefined { return this.pokedex.find(pokemon => pokemon.id === id); }
addPokemon(pokemon: Pokemon): void { this.pokedex.push(pokemon); }
updatePokemon(pokemon: Pokemon): void { const index = this.pokedex.findIndex(p => p.id === pokemon.id); if (index !== -1) { this.pokedex[index] = pokemon; } } }
TrainerService.ts
```typescript // src/services/TrainerService.ts import { Trainer } from '../models/Trainer'; import { Pokemon } from '../models/Pokemon'; import * as trainerData from '../data/trainers.json';
export class TrainerService { private trainers: Trainer[] = trainerData;
getTrainerByName(name: string): Trainer | undefined { return this.trainers.find(trainer => trainer.name === name); }
addTrainer(trainer: Trainer): void { this.trainers.push(trainer); }
addPokemonToTrainer(trainerName: string, pokemon: Pokemon): boolean { const trainer = this.getTrainerByName(trainerName); if (trainer) { trainer.addPokemon(pokemon); return true; } return false; } }
4. JSON Data for Pokedex and Trainers
#### `pokedex.json`
[ { "id": 1, "name": "Bulbasaur", "type": ["Grass", "Poison"], "level": 5, "evolved": false, "evolutions": ["Ivysaur", "Venusaur"], "battleStats": { "wins": 0, "losses": 0, "draws": 0 } }, { "id": 2, "name": "Charmander", "type": ["Fire"], "level": 5, "evolved": false, "evolutions": ["Charmeleon", "Charizard"], "battleStats": { "wins": 0, "losses": 0, "draws": 0 } } ]
#### `trainers.json`
[ { "id": 1, "name": "Ash", "pokemons": [] }, { "id": 2, "name": "Misty", "pokemons": [] }, { "id": 3, "name": "Brock", "pokemons": [] } ] ```
5. Main Application (`index.ts`)
// src/index.ts import { TrainerService } from './services/TrainerService'; import { PokedexService } from './services/PokedexService'; import { Battle } from './models/Battle';
const trainerService = new TrainerService(); const pokedexService = new PokedexService();
const ash = trainerService.getTrainerByName("Ash"); const bulbasaur = pokedexService.getPokemonById(1); const charmander = pokedexService.getPokemonById(2);
if (ash && bulbasaur && charmander) { trainerService.addPokemonToTrainer("Ash", bulbasaur); trainerService.addPokemonToTrainer("Ash", charmander);
const battleResult = Battle.conductBattle(bulbasaur, charmander); console.log(`Battle Result:`, battleResult); }
megaphone

types.ts` — Let us define `types.ts` with common types and enums that can be shared across the application, ensuring consistency and simplifying the addition of new functionality in the future.

Here's the code for `types.ts`:
// src/utils/types.ts
// Enum for Battle Result Types export enum BattleResultType { WIN = 'win', LOSS = 'loss', DRAW = 'draw', }
// Interface for Battle Stats export interface BattleStats { wins: number; losses: number; draws: number; }
// Enum for Pokémon Types export enum PokemonType { GRASS = 'Grass', POISON = 'Poison', FIRE = 'Fire', WATER = 'Water', ELECTRIC = 'Electric', ROCK = 'Rock', // Add more as needed }
// General interface for Pokémon Evolution Stages export interface EvolutionStage { stage: number; evolvedName: string; }
// Utility Type for Pokémon Attributes export interface PokemonAttributes { id: number; name: string; types: PokemonType[]; level: number; evolved: boolean; evolutions: EvolutionStage[]; battleStats: BattleStats; }
Explanation of `types.ts` Components
- Enums - `BattleResultType`: A simple enum for defining battle outcomes (`WIN`, `LOSS`, `DRAW`). This improves readability when recording battle results. - `PokemonType`: An enum listing Pokémon types (e.g., `GRASS`, `FIRE`), useful for filtering and consistency.
- Interfaces enforce interoperability among classes in our code base. Because if I apply an interface to a class: this guarentees that this class will present the APIs (methods) specified in the Interface.
- `BattleStats`: Defines a structure for tracking a Pokémon's battle results. It simplifies updating win/loss/draw statistics across the application. - `EvolutionStage`: Specifies each evolutionary stage a Pokémon might reach, with properties for stage level and evolved name. - `PokemonAttributes`: A general structure that standardizes the essential properties of a Pokémon, allowing for flexible sharing and extension within different application modules.
Using this `types.ts` file, we achieve modularity, making it easier to add new Pokémon types or additional battle result types, should the domain evolve. Additionally, by referencing these types across the project, developers will find it easier to adapt to changes, enhancing Agile flexibility and enabling better traceability for DevOps practices.

info

Run and if necessary do PD on your Pokemon TypeScript Application

Lab Instruction: Compiling and Running TypeScript Code

To run your TypeScript application natively using ts-node, follow these steps:


Install ts-node: If you haven't already, install ts-node globally or locally in your project:
npm install -g ts-node
or yarn global add ts-node Use code with caution.
or locally in your project:
npm install --save-dev ts-node Use code with caution.
or yarn add --dev ts-node Use code with caution.
Run your application: You can use ts-node directly to run your main TypeScript file (e.g., index.ts):
ts-node src/index.ts
This command will execute your TypeScript code without the need for manual compilation to JavaScript. ts-node handles the compilation and execution in one step.
megaphone

Problem Determination:

The error message you are seeing indicates that TypeScript cannot find or properly import the JSON module.
The solution lies in ensuring TypeScript knows how to handle JSON imports.
To fix this, you need to adjust both your TypeScript configuration and your code.

Solution Steps:

1. **Enable JSON Module Resolution in `tsconfig.json`**
Ensure your `tsconfig.json` file has the `resolveJsonModule` and `esModuleInterop` options enabled.
Update your `tsconfig.json` to include these options: { "compilerOptions": { "module": "commonjs", "esModuleInterop": true, "resolveJsonModule": true, // other options... } }
These settings allow TypeScript to import JSON files and properly handle modules.
2. **Check the Import Statement**: Make sure your import statement is correctly formatted. Use a default import if necessary:
```typescript import trainerData from '../data/trainers.json'; ```
This format works with the `resolveJsonModule` and `esModuleInterop` settings to allow JSON imports.
### Final Code Adjustment: Update `TrainerService.ts` to: ```typescript import trainerData from '../data/trainers.json';
// Now you can use `trainerData` as needed ```
### Additional Steps to Ensure Correct Handling: - **Restart TypeScript Compiler**: If you are using a TypeScript watch mode or development server, restart it to make sure the new configuration is applied. - **Reinstall Node Modules (Optional)**: Sometimes, running `npm install` can help ensure all necessary modules are correctly set up.
### Verification: Once these changes are made, try running your TypeScript code again. It should now recognize and properly import the `trainers.json` file without errors.

Note: The purpose for these drills is to train you to identify problems so that when you run JEST tests, you will be familar with the kinds of errors that will arise:
Engaging in problem determination drills may feel challenging and time-consuming at first, but this investment will yield substantial dividends as you progress to running JEST tests.
Understanding how to diagnose and resolve errors effectively equips you with the foresight needed to anticipate and handle issues that arise in automated testing. When you run your JEST tests, you'll find that the skills you've honed in troubleshooting code will make test results clearer and more manageable.
By mastering problem identification now, you’re building a foundation of resilience and competence that will not only make testing smoother but will also instill confidence when debugging complex applications.
Remember, every error you resolve today is one less unknown you'll face tomorrow. So keep pushing through, and know that each step brings you closer to becoming a proficient, adaptive Dev Ops Engineer.
Your future self—and your CI/CD pipeline—will thank you!

info

Problem determination Point 2

The error shown in the trace message indicates that TypeScript is trying to assign `trainerData` (likely parsed directly from JSON) to a `Trainer[]` type.
However, the JSON data is not being recognized as an array of `Trainer` objects with the correct methods (`addPokemon`, `getPokemon`).
Instead, TypeScript sees it as plain objects missing these properties.

Explanation ​JSON data, when imported, is treated as plain objects that match the structure but do not have the methods or instance properties defined in the `Trainer` class.

To solve this, you need to instantiate the `Trainer` class using the JSON data.

Solution Steps

1. **Map JSON Data to Class Instances**
Modify the code that initializes `this.trainers` so that it maps the plain objects from `trainerData` to instances of the `Trainer` class.

2. **Adjust `TrainerService.ts`**:
Update the `TrainerService` constructor or initialization logic to create actual `Trainer` objects from the imported data.

Code Fix
Update your `TrainerService.ts` to map the plain JSON objects to `Trainer` instances:
Here is the complete working TrainerService.ts with the necessary adjustments to handle JSON data properly and ensure that the data is mapped into Trainer class instances:

// src/services/TrainerService.ts
import { Trainer } from '../models/Trainer';
import trainerData from '../data/trainers.json';

export class TrainerService {
private trainers: Trainer[];

constructor() {
// Map plain JSON objects to Trainer instances
this.trainers = trainerData.map(
data => new Trainer(data.id, data.name, data.pokemons)
);
}

// Method to get a trainer by name
getTrainerByName(name: string): Trainer | undefined {
return this.trainers.find(trainer => trainer.name.toLowerCase() === name.toLowerCase());
}

// Method to add a new trainer
addTrainer(trainer: Trainer): void {
this.trainers.push(trainer);
}

// Method to add a Pokemon to a trainer's collection
addPokemonToTrainer(trainerName: string, pokemon: any): boolean {
const trainer = this.getTrainerByName(trainerName);
if (trainer) {
trainer.addPokemon(pokemon);
return true;
}
return false;
}

// Method to get all trainers
getAllTrainers(): Trainer[] {
return this.trainers;
}
}

Explanation of the Code:

Initialization: The constructor initializes the trainers array by mapping trainerData (parsed from trainers.json) to instances of the Trainer class. This ensures that the Trainer objects have access to their methods like addPokemon and getPokemon.
getTrainerByName(): A method that searches for a trainer by name (case-insensitive) and returns the corresponding Trainer object if found.
addTrainer(): A method to add a new Trainer instance to the trainers array.
addPokemonToTrainer(): A method that locates a trainer by name and adds a specified Pokemon to their collection if the trainer exists.
getAllTrainers(): Returns the list of all Trainer instances in the service.

Assumptions:

The Trainer class must have a constructor that accepts id, name, and pokemons as parameters.
The pokemons parameter in trainerData is an array of any type (can be refined with a specific Pokemon type if defined).

Trainer.ts for Context:

Ensure your Trainer.ts is structured as follows:
typescript
Copy code
// src/models/Trainer.ts

export class Trainer {
constructor(
public id: number,
public name: string,
public pokemons: any[] = [] // Replace `any[]` with your specific Pokemon type if available
) {}

addPokemon(pokemon: any): void {
Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.