DEV Community

David
David

Posted on

Quick Example of Abstract Classes in TypeScript

In this example we abstract a Card class so we can have Playing cards or Joker Cards
If we were to go further we could create a Game class that accepted one or more of the Card types depending on the game, blackjack, poker, war, etc…

Interfaces are not required for abstract classes, but can be useful for setting up a root blueprint

interface ICard {
  value: number;
  getValue: ()=>number;
  compareTo: (o: ICard) => boolean;
  toString: () => string;
}
Enter fullscreen mode Exit fullscreen mode

Abstract class can instantiate its own methods and variables or declare them as required for classes that extend from it.

abstract class Card implements ICard {
  value: number;
  constructor(value: number){
    this.value = value;
  }
  getValue(){
    return this.value;
  }
  compareTo(o: Card){
    return this.getValue() > o.getValue();
  }


  // abstract keyword is used to tell classes that extend this class
  // that they have to implement this method
  abstract toString(): string;
}

// Enum is just for setting the type in the next class PlayingCard
enum SUIT {
  Diamonds,
  Clubs,
  Spades,
  Hearts,
}
Enter fullscreen mode Exit fullscreen mode

Extending an abstract class means we only have to implement the methods of Card that are marked abstract after that we can customize the methods to fit our needs

class PlayingCard extends Card {
  suit: SUIT;

  constructor(value: number, suit: string){
    // since we are extending an abstract class we need to instantiate the   
    // Card class in the constructor with super();
    super(value);
    this.suit = this.convertStringToSuit(suit);
  }

  private convertStringToSuit(suit: string){
    switch(suit){
      case "Diamonds":
        return SUIT.Diamonds;
      case "Clubs":
        return SUIT.Clubs;
      case "Spades":
        return SUIT.Spades;
      case "Hearts":
        return SUIT.Hearts;
      default:
        throw new Error();
    }
  }

  private convertSuitToString(){
    switch(this.suit){
      case SUIT.Diamonds:
        return "Diamonds";
      case SUIT.Clubs:
        return "Clubs";
      case SUIT.Spades:
        return "Spades";
      case SUIT.Hearts:
        return "Hearts";
      default:
        return "";
    }
  }

  toString(): string {
    return String(this.getValue()) + " of " + this.convertSuitToString();
  }
}
Enter fullscreen mode Exit fullscreen mode
A new class based on Card class that has a unchanging value, but its own color method
class JokerCard extends Card {
  color: string;
  constructor(color: string){
    super(14);
    this.color = color;
  }

  toString(): string {
    return String(this.value) + " " + this.color;
  }
}
Enter fullscreen mode Exit fullscreen mode

Instantiating and using both of the classes example

Card is the Abstract class so now we can technically store multiple types of Card objects into a single array

const cards: Card[] = [];
cards.push(new PlayingCard(1, "Diamonds"));
cards.push(new JokerCard("Black"))
Enter fullscreen mode Exit fullscreen mode

Due to shared Logic between the abstract class Card and PlayingCard and JokerCard the compareTo method is available for comparing the playingcard and JokerCard class

console.log(cards[0].compareTo(cards[1])); 
// returns false as 1 is not greater then 14
Enter fullscreen mode Exit fullscreen mode

The toString method is also allowed no matter which class object as both were required to implement it.

console.log(cards[0].toString()) 
// prints "1 of Diamonds"
console.log(cards[1].toString()) 
// prints "14 Black"
Enter fullscreen mode Exit fullscreen mode

I’ve also implemented this in an online editor to see the working example and edit it goto Card abstractions

Top comments (0)