Kurz: TypeScript ergänzt JavaScript-Klassen um Typen, Sichtbarkeit und abstrakte Klassen.
Teil des Kurses TypeScript
Kapitel 7 von 15 im Kurs TypeScript (Abschnitt „Fortgeschritten“). Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.
TypeScript ergänzt JavaScript-Klassen um Typen, Sichtbarkeit und abstrakte Klassen.
Eigenschaften und Methoden
class Konto {
readonly inhaber: string;
#stand: number = 0; // echt privat (JavaScript)
private log: string[] = []; // nur zur Compile-Zeit privat (TypeScript)
constructor(inhaber: string, startwert = 0) {
this.inhaber = inhaber;
this.#stand = startwert;
}
einzahlen(betrag: number): this {
if (betrag <= 0) throw new RangeError("Betrag muss positiv sein");
this.#stand += betrag;
this.log.push(`+${betrag}`);
return this;
}
get stand(): number {
return this.#stand;
}
verlauf(): string[] {
return [...this.log];
}
}
const k = new Konto("Mia", 100).einzahlen(50).einzahlen(25);
console.log(k.inhaber, k.stand, k.verlauf());Ausgabe:
Mia 175 [ '+50', '+25' ]| Modifikator | Wirkung |
|---|---|
public | überall sichtbar (Standard) |
private | nur in der Klasse (Compile-Zeit) |
protected | in der Klasse und Unterklassen |
readonly | nur im Konstruktor zuweisbar |
#name | echt privat zur Laufzeit |
static | gehört zur Klasse |
class A { private geheim = 1; }
const a = new A();
console.log(a.geheim);Fehlermeldung:
error TS2341: Property 'geheim' is private and only accessible within class 'A'.Interfaces umsetzen
Eine Klasse kann einen Vertrag erfüllen müssen:
interface Form {
flaeche(): number;
readonly name: string;
}
class Kreis implements Form {
readonly name = "Kreis";
constructor(private radius: number) {}
flaeche(): number { return Math.PI * this.radius ** 2; }
}Hinweis
Die Kurzschreibweise
constructor(private radius: number)(Parameter-Eigenschaften) ist praktisch, wird aber von manchen Werkzeugen, die Typen nur entfernen, nicht unterstützt. Ausführlich schreiben ist die portable Variante.
interface Form { flaeche(): number; readonly name: string }
class Rechteck implements Form {
readonly name = "Rechteck";
breite: number;
hoehe: number;
constructor(breite: number, hoehe: number) { this.breite = breite; this.hoehe = hoehe; }
flaeche(): number { return this.breite * this.hoehe; }
}
class Quadrat extends Rechteck {
constructor(seite: number) { super(seite, seite); }
}
const formen: Form[] = [new Rechteck(2, 3), new Quadrat(4)];
console.log(formen.map((f) => f.flaeche()));Ausgabe:
[ 6, 16 ]Abstrakte Klassen
Eine abstrakte Klasse ist nur Vorlage und kann nicht direkt instanziiert werden:
abstract class Tier {
constructor(protected name: string) { }
abstract laut(): string; // Unterklassen müssen das umsetzen
vorstellen(): string { return `${this.name} macht ${this.laut()}`; }
}In reinem JavaScript-Stil, damit er auch ohne Compiler läuft:
abstract class Tier {
name: string;
constructor(name: string) { this.name = name; }
abstract laut(): string;
vorstellen(): string { return `${this.name} macht ${this.laut()}`; }
}
class Hund extends Tier { laut() { return "Wuff"; } }
class Katze extends Tier { laut() { return "Miau"; } }
for (const t of [new Hund("Rex"), new Katze("Mimi")]) console.log(t.vorstellen());Ausgabe:
Rex macht Wuff
Mimi macht Miauabstract class Tier { abstract laut(): string }
const t = new Tier();Fehlermeldung:
error TS2511: Cannot create an instance of an abstract class.Statische Member und Getter/Setter
class Temperatur {
static readonly ABSOLUTER_NULLPUNKT = -273.15;
#celsius = 0;
get celsius(): number { return this.#celsius; }
set celsius(w: number) {
if (w < Temperatur.ABSOLUTER_NULLPUNKT) throw new RangeError("zu kalt");
this.#celsius = w;
}
get fahrenheit(): number { return this.#celsius * 9 / 5 + 32; }
static ausFahrenheit(f: number): Temperatur {
const t = new Temperatur();
t.celsius = (f - 32) * 5 / 9;
return t;
}
}
const t = Temperatur.ausFahrenheit(212);
console.log(t.celsius, t.fahrenheit);Ausgabe:
100 212Klassen als Typ
Der Name einer Klasse ist zugleich ein Typ für ihre Instanzen:
class Punkt { constructor(public x = 0, public y = 0) {} }Auch hier gilt: strukturelle Typisierung. Ein Objekt { x: 1, y: 2 } passt zum Typ Punkt, obwohl es keine Instanz ist.
Composition over Inheritance
Vererbung koppelt Klassen eng. Oft besser ist Komposition: Eine Klasse bekommt andere Objekte über das Interface und nutzt sie.
interface Speicher { speichere(k: string, v: string): void; lade(k: string): string | undefined }
class MemSpeicher implements Speicher {
private daten = new Map<string, string>();
speichere(k: string, v: string) { this.daten.set(k, v); }
lade(k: string) { return this.daten.get(k); }
}
class Einstellungen {
speicher: Speicher;
constructor(speicher: Speicher) { this.speicher = speicher; } // Abhängigkeit hereingegeben
setzeThema(t: "hell" | "dunkel") { this.speicher.speichere("thema", t); }
thema() { return this.speicher.lade("thema") ?? "hell"; }
}
const e = new Einstellungen(new MemSpeicher());
console.log(e.thema());
e.setzeThema("dunkel");
console.log(e.thema());Ausgabe:
hell
dunkelIm Test kannst du jederzeit einen anderen Speicher hineingeben, im Browser etwa localStorage.
Merke
- Sichtbarkeit:
public,private,protected,readonly;#nameist echt privat implementserzwingt, dass eine Klasse ein Interface erfülltabstractfür Vorlagen;staticfür Klassenebene- Klassen sind strukturell typisiert
- Komposition und Schnittstellen halten Code flexibel und testbar
Übungsaufgabe
Baue eine abstrakte Klasse Mitarbeiter mit gehalt() und zwei Unterklassen (Angestellter, Freelancer) und summiere die Gehälter einer gemischten Liste.
Quiz zur Selbstkontrolle
Was bewirkt private in TypeScript?
- Nur innerhalb der Klasse zugreifbar, geprüft beim Kompilieren (richtig)
- Echt privat zur Laufzeit
- Nur für Unterklassen
- Wie static
Was ist eine abstrakte Klasse?
- Eine Vorlage, die nicht direkt instanziiert werden kann (richtig)
- Eine Klasse ohne Methoden
- Eine Klasse im Browser
- Eine private Klasse
Wofür ist implements?
- Die Klasse muss die Form eines Interfaces erfüllen (richtig)
- Die Klasse erbt Code
- Die Klasse wird importiert
- Die Klasse wird gelöscht
Weiter im Kurs
Zurück: Arrays, Tupel und Enums
Weiter: Generics
Alle Kapitel: TypeScript im Überblick