Vererbung in TypeScript
Lerne Vererbung mit extends, super und Method Overriding in TypeScript-Klassen.
Aktualisiert:
Vererbung in TypeScript
Vererbung ermöglicht es Klassen, Eigenschaften und Methoden von anderen Klassen zu erben. TypeScript erweitert JavaScript-Vererbung um bessere Typisierung.
Grundlagen der Vererbung
// Basisklasse
class Animal {
constructor(public name: string) {}
speak(): string {
return `${this.name} makes a sound`;
}
move(distance: number): string {
return `${this.name} moved ${distance} meters`;
}
}
// Abgeleitete Klasse
class Dog extends Animal {
constructor(name: string, public breed: string) {
super(name); // Konstruktor der Basisklasse aufrufen
}
// Methode überschreiben
speak(): string {
return `${this.name} barks: Woof!`;
}
// Neue Methode
fetch(): string {
return `${this.name} fetches the ball`;
}
}
const dog = new Dog("Buddy", "Golden Retriever");
console.log(dog.speak()); // "Buddy barks: Woof!"
console.log(dog.move(10)); // "Buddy moved 10 meters" (geerbt)
console.log(dog.fetch()); // "Buddy fetches the ball"
super Keyword
super im Konstruktor
class Person {
constructor(
public firstName: string,
public lastName: string
) {}
get fullName(): string {
return `${this.firstName} ${this.lastName}`;
}
}
class Employee extends Person {
constructor(
firstName: string,
lastName: string,
public employeeId: string,
public department: string
) {
// super() muss vor this aufgerufen werden!
super(firstName, lastName);
}
getInfo(): string {
return `${this.fullName} (${this.employeeId}) - ${this.department}`;
}
}
const employee = new Employee("Max", "Mustermann", "E001", "Engineering");
console.log(employee.getInfo()); // "Max Mustermann (E001) - Engineering"
super für Methoden
class Vehicle {
constructor(public brand: string) {}
start(): string {
return `${this.brand} is starting...`;
}
stop(): string {
return `${this.brand} has stopped`;
}
}
class Car extends Vehicle {
constructor(
brand: string,
public model: string
) {
super(brand);
}
// Erweitert die Eltern-Methode
start(): string {
const parentMessage = super.start(); // Eltern-Methode aufrufen
return `${parentMessage} Engine running!`;
}
}
const car = new Car("BMW", "M3");
console.log(car.start()); // "BMW is starting... Engine running!"
Method Overriding
Grundlegendes Überschreiben
class Shape {
constructor(public color: string) {}
getArea(): number {
return 0;
}
describe(): string {
return `A ${this.color} shape`;
}
}
class Rectangle extends Shape {
constructor(
color: string,
public width: number,
public height: number
) {
super(color);
}
// Override
getArea(): number {
return this.width * this.height;
}
// Override mit super
describe(): string {
return `${super.describe()} - Rectangle ${this.width}x${this.height}`;
}
}
class Circle extends Shape {
constructor(
color: string,
public radius: number
) {
super(color);
}
getArea(): number {
return Math.PI * this.radius ** 2;
}
describe(): string {
return `${super.describe()} - Circle with radius ${this.radius}`;
}
}
const rect = new Rectangle("blue", 10, 5);
const circle = new Circle("red", 7);
console.log(rect.getArea()); // 50
console.log(circle.getArea()); // 153.94...
console.log(rect.describe()); // "A blue shape - Rectangle 10x5"
override Keyword (TypeScript 4.3+)
class Animal {
speak(): void {
console.log("Some sound");
}
}
class Dog extends Animal {
override speak(): void { // Explizit markieren
console.log("Woof!");
}
// Fehler mit noImplicitOverride: true
// override run(): void {} // Error: This member cannot have an 'override' modifier
}
Aktiviere in tsconfig.json:
{
"compilerOptions": {
"noImplicitOverride": true
}
}
Polymorphismus
class Animal {
constructor(public name: string) {}
speak(): string {
return `${this.name} makes a sound`;
}
}
class Dog extends Animal {
speak(): string {
return `${this.name}: Woof!`;
}
}
class Cat extends Animal {
speak(): string {
return `${this.name}: Meow!`;
}
}
class Bird extends Animal {
speak(): string {
return `${this.name}: Tweet!`;
}
}
// Polymorphismus: Verschiedene Typen, gleiche Schnittstelle
const animals: Animal[] = [
new Dog("Buddy"),
new Cat("Whiskers"),
new Bird("Tweety")
];
animals.forEach(animal => {
console.log(animal.speak());
});
// "Buddy: Woof!"
// "Whiskers: Meow!"
// "Tweety: Tweet!"
instanceof und Type Guards
class Vehicle {
drive(): void {
console.log("Driving...");
}
}
class Car extends Vehicle {
honk(): void {
console.log("Beep beep!");
}
}
class Motorcycle extends Vehicle {
wheelie(): void {
console.log("Doing a wheelie!");
}
}
function handleVehicle(vehicle: Vehicle): void {
vehicle.drive();
if (vehicle instanceof Car) {
vehicle.honk(); // TypeScript weiß: vehicle ist Car
} else if (vehicle instanceof Motorcycle) {
vehicle.wheelie(); // TypeScript weiß: vehicle ist Motorcycle
}
}
handleVehicle(new Car()); // "Driving..." "Beep beep!"
handleVehicle(new Motorcycle()); // "Driving..." "Doing a wheelie!"
Mehrfach-Vererbung (nicht direkt möglich)
TypeScript unterstützt keine Mehrfach-Vererbung, aber Mixins:
// Mixin Pattern
type Constructor<T = {}> = new (...args: any[]) => T;
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
createdAt = new Date();
updatedAt = new Date();
touch() {
this.updatedAt = new Date();
}
};
}
function Activatable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
isActive = true;
activate() {
this.isActive = true;
}
deactivate() {
this.isActive = false;
}
};
}
// Basis-Klasse
class User {
constructor(public name: string) {}
}
// Mixins anwenden
const TimestampedUser = Timestamped(User);
const FullUser = Activatable(TimestampedUser);
const user = new FullUser("Max");
console.log(user.name); // "Max"
console.log(user.createdAt); // Date
console.log(user.isActive); // true
user.deactivate();
user.touch();
Praktische Beispiele
UI Component Hierarchy
abstract class Component {
protected container: HTMLElement | null = null;
constructor(protected selector: string) {}
abstract render(): string;
mount(): void {
this.container = document.querySelector(this.selector);
if (this.container) {
// Sicheres Rendering mit textContent für einfachen Text
this.container.textContent = this.render();
}
}
}
class Message extends Component {
constructor(
selector: string,
private text: string
) {
super(selector);
}
render(): string {
return this.text;
}
}
class Counter extends Component {
private count = 0;
constructor(selector: string) {
super(selector);
}
render(): string {
return `Count: ${this.count}`;
}
increment(): void {
this.count++;
this.mount(); // Re-render
}
}
Repository Pattern mit Vererbung
abstract class BaseRepository<T extends { id: number }> {
protected items: T[] = [];
findAll(): T[] {
return [...this.items];
}
findById(id: number): T | undefined {
return this.items.find(item => item.id === id);
}
create(item: T): T {
this.items.push(item);
return item;
}
delete(id: number): boolean {
const index = this.items.findIndex(item => item.id === id);
if (index !== -1) {
this.items.splice(index, 1);
return true;
}
return false;
}
abstract validate(item: T): boolean;
}
interface User {
id: number;
name: string;
email: string;
}
class UserRepository extends BaseRepository<User> {
validate(user: User): boolean {
return user.name.length > 0 && user.email.includes("@");
}
override create(user: User): User {
if (!this.validate(user)) {
throw new Error("Invalid user data");
}
return super.create(user);
}
findByEmail(email: string): User | undefined {
return this.items.find(user => user.email === email);
}
}
Zusammenfassung
- extends: Klasse von anderer Klasse ableiten
- super(): Konstruktor der Elternklasse aufrufen
- super.method(): Eltern-Methode aufrufen
- override: Methode explizit überschreiben
- Polymorphismus: Verschiedene Typen, gleiche Schnittstelle
- instanceof: Type Guard für Klassenhierarchien
Im nächsten Kapitel lernst du abstrakte Klassen!