# Factory Pattern in TypeScript

Source: https://tpiros.dev/blog/factory-pattern-in-typescript

Design patterns help us write better software. They're reusable solutions to problems that keep showing up, and they've been around long enough to prove their worth.

# GoF

Back in 1994, Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides published a book covering 23 design patterns: Design Patterns: Elements of Reusable Object-Oriented Software. You might know it (or its authors) as the Gang of Four (GoF).

The patterns they laid out still hold up. Every one of them applies to modern applications.

# Design Pattern types

Design patterns fall into these categories based on what they aim to achieve:

- Creational: deal with object creation
- Structural: deal with relationships between objects (or other entities)
- Behavioural: deal with communication patterns between objects
- Concurrency: deal with multi-threaded paradigms

# Factory Pattern

The factory pattern is a creational design pattern. It provides a generic interface to create objects. It works with a **creator** that produces **products**. When you use the factory pattern, you don't need the `new` keyword because instantiation happens inside the factory itself.

## When to use the factory pattern?

The factory pattern fits situations where the object creation process is complex, or where multiple objects share the same properties.

## TypeScript implementation

The factory pattern could be implemented in any language, including vanilla JavaScript. But I thought it'd be interesting to see how a TypeScript version would look.

> Should you want the JavaScript version, just transpile the TypeScript code to JavaScript, either to ES2015 or to ES5.

## Factory example

Let's start with a simple factory pattern example. We've got a factory that creates superheroes:

```typescript
// herofactory.ts

  private name: string;
  private health: number;

  constructor(name: string, health: number) {
    this.name = name;
    this.health = health;
  }

  introduce() {
    console.log(`Hello, I am ${this.name}. Pleasure to meet you.`);
  }
}

  return new HeroFactory(name, health);
}
```

Using this class is dead simple:

```typescript
// app.ts

const spiderman = heroFactory.createHero('Peter', 100);
spiderman.introduce(); // Hello, I am Peter. Pleasure to meet you.

const superman = heroFactory.createHero('Clark', 100);
superman.introduce(); // Hello, I am Clark. Pleasure to meet you.
```

Let's take this further.

Say we're building a game where we can create superheroes. A superhero can be either a good guy (a Hero) or a bad guy (a Villain). These two are represented as different classes:

```typescript
// hero.ts

  private name: string;
  private health: number;
  private maxHealth: number = 100;
  constructor(name: string, health: number = 100) {
    this.name = name;
    if (health < this.maxHealth) {
      this.health = health;
    } else {
      this.health = this.maxHealth;
    }
  }

  attacked(attackValue) {
    if (attackValue > this.health) {
      console.log(`${this.name} is no more.`);
    } else {
      this.health -= attackValue;
      console.log(`${this.name} attacked: ${attackValue} -- ${this.health}`);
    }
  }

  heal(healValue) {
    if (this.health + healValue > this.maxHealth) {
      this.health = this.maxHealth;
      console.log(`${this.name} has max health of ${this.maxHealth}`);
    } else {
      this.health += healValue;
      console.log(`${this.name} healed to ${this.health}`);
    }
  }
}

// villain.ts

  private name: string;
  private health: number;
  private maxHealth: number = 100;
  constructor(name: string, health: number = 100) {
    this.name = name;
    if (health < this.maxHealth) {
      this.health = health;
    } else {
      this.health = this.maxHealth;
    }
  }

  rampage() {
    if (this.health <= 10) {
      this.health = this.maxHealth * 0.9;
      console.log(`${this.name} restored health to ${this.health}`);
    } else {
      console.log(`${this.name} is not weak enough`);
    }
  }

  attacked(attackValue) {
    if (attackValue > this.health) {
      console.log(`${this.name} is no more.`);
    } else {
      this.health -= attackValue;
      console.log(`${this.name} attacked: ${attackValue} -- ${this.health}`);
    }
  }

  heal(healValue) {
    if (this.health + healValue > this.maxHealth) {
      this.health = this.maxHealth;
      console.log(`${this.name} has max health of ${this.maxHealth}`);
    } else {
      this.health += healValue;
      console.log(`${this.name} healed to ${this.health}`);
    }
  }
}
```

Now we create a SuperHeroFactory:

```typescript
// superherofactory.ts

  createSuperHero(type: Object);
  createSuperHero(type: 'hero'): Hero;
  createSuperHero(type: 'villain'): Villain;

  public createSuperHero(heroOptions): Hero | Villain {
    if (heroOptions.type === 'hero') {
      const hero = new Hero(heroOptions.name, heroOptions.health);
      return hero;
    } else if (heroOptions.type === 'villain') {
      const villain = new Villain(heroOptions.name, heroOptions.health);
      return villain;
    } else {
      throw new Error('Select either a Hero or a Villain');
    }
  }
}
```

Notice the specialised signatures above. If a hero is created we return the `Hero` class, if a villain is created we return the `Villain` class. We need these to ensure the correct methods are available. Leaving them out would mean the `rampage()` method becomes inaccessible: `Property 'rampage' does not exist on type 'Hero | Villain'. Property 'rampage' does not exist on type 'Hero'.` TypeScript would assume `joker` is of type `Hero`, which is wrong.

Finally, we implement the factory:

```typescript
// app.ts

const superheroFactory = new SuperHeroFactory();
const batman = superheroFactory.createSuperHero({
  name: 'Batman',
  type: 'hero',
});
const joker = superheroFactory.createSuperHero({
  name: 'Joker',
  health: 50,
  type: 'villain',
});

batman.attacked(40); // Batman attacked: 40 -- 60
joker.attacked(40); // Joker attacked: 40 -- 10
joker.rampage(); // Joker restored health to 90
```
