Design a TypeScript class hierarchy for geometric shapes like Circle, Rectangle, and Triangle. Each shape should have a method to calculate its area and perimeter. Also, create a function that accepts an array of shapes and calculates the total area and perimeter of all shapes combined.
PLAINFORMAT
abstract class Shape {
  abstract area(): number;
  abstract perimeter(): number;
}

class Circle extends Shape {
  constructor(private radius: number) {
    super();
  }

  area(): number {
    return Math.PI * this.radius ** 2;
  }

  perimeter(): number {
    return 2 * Math.PI * this.radius;
  }
}

class Rectangle extends Shape {
  constructor(private width: number, private height: number) {
    super();
  }

  area(): number {
    return this.width * this.height;
  }

  perimeter(): number {
    return 2 * (this.width + this.height);
  }
}

class Triangle extends Shape {
  constructor(
    private sideA: number,
    private sideB: number,
    private sideC: number
  ) {
    super();
  }

  area(): number {
    const s = this.perimeter() / 2;
    return Math.sqrt(s * (s - this.sideA) * (s - this.sideB) * (s - this.sideC));
  }

  perimeter(): number {
    return this.sideA + this.sideB + this.sideC;
  }
}

function calculateTotalAreaAndPerimeter(shapes: Shape[]): { totalArea: number; totalPerimeter: number } {
  let totalArea = 0;
  let totalPerimeter = 0;

  for (const shape of shapes) {
    totalArea += shape.area();
    totalPerimeter += shape.perimeter();
  }

  return { totalArea, totalPerimeter };
}

// Example usage:
const circle = new Circle(5);
const rectangle = new Rectangle(4, 6);
const triangle = new Triangle(3, 4, 5);

const shapes: Shape[] = [circle, rectangle, triangle];
const result = calculateTotalAreaAndPerimeter(shapes);

console.log(`Total area: ${result.totalArea}`);
console.log(`Total perimeter: ${result.totalPerimeter}`);