Aufwärmen · Aufgabe 1 von 7
// B3.3 · ca. 30 Min. · Einstieg
instanceof und Type Predicates
Nach dieser Lektion grenzen Sie mit instanceof ein, nutzen einen gefangenen Fehler sicher, sagen den Typ einer Variablen nach Zuweisungen und returns voraus und schreiben ein Type Predicate, das auch Arrays filtert.
Danach können Sie
- Mit instanceof eingrenzen, auch den unknown-Wert in einer catch-Klausel
- Den Typ einer Variablen nach Zuweisungen, returns und throws vorhersagen (Kontrollflussanalyse)
- Type Predicates wie pet is Fish schreiben und Arrays damit filtern
Vorhersagen · Aufgabe 2 von 7
Sagen Sie es vorher, bevor Sie weiterlesen: values hat den Typ (number | null)[]. Was passiert bei npx tsc und node main.ts?
const values = [3, null, 7, null]; const numbers = values.filter((v) => v !== null); console.log(numbers.map((n) => n * 2));Üben · Aufgabe 3 von 7
Setzen Sie den Rückgabetyp von isFish ein, damit tsc move annimmt und das Programm swims und flies ausgibt.
type Fish = { name: string; swim: () => string }; type Bird = { name: string; fly: () => string }; type Pet = Fish | Bird; function isFish(pet: Pet): ____ { return "swim" in pet; } function move(pet: Pet): string { return isFish(pet) ? pet.swim() : pet.fly(); } console.log(move({ name: "Nemo", swim: () => "swims" })); console.log(move({ name: "Tweety", fly: () => "flies" }));function isFish(pet: Pet): {Üben · Aufgabe 4 von 7
Ordnen Sie mit den Typen unten jeder Situation den Typ zu, den die Variable an dieser Stelle hat.
type Fish = { swim: () => void }; type Bird = { fly: () => void }; function isFish(pet: Fish | Bird): pet is Fish { return "swim" in pet; }Üben · Aufgabe 5 von 7
Welche Zeilen lehnt tsc ab?
type Id = string | number; let id: Id = "a7"; id = 7; console.log(id.toFixed(1)); id = "b9"; console.log(id.toUpperCase()); id = true;Denksport · Aufgabe 6 von 7
Knobelaufgabe. tsc meldet für diese Datei keine Fehler. Was passiert beim Ausführen?
function isNumber(x: unknown): x is number { return typeof x === "string"; } const input: unknown = "42"; if (isNumber(input)) { console.log(input.toFixed(1)); }Anwenden · Aufgabe 7 von 7
Kleine Aufgabe. Ein Blog hat Entwürfe ({ title }) und veröffentlichte Beiträge ({ title, url }). Schreiben Sie ein Type Predicate isPublished und links(posts), das mit filter(isPublished) für jeden veröffentlichten Beitrag "title: url" liefert. Schreiben Sie dann countPosts(text), das JSON-Text parst und die Länge des Arrays liefert, sonst 0; prüfen Sie im catch mit instanceof SyntaxError, bevor Sie den Fehler lesen. Prüfen Sie es im Editor oder mit npx tsc und node main.ts.
Prüfen Sie Ihr Ergebnis anhand dieser Liste
Selbst programmieren
Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.
Ausgearbeitetes Beispiel
Ein Ereignisprotokoll
Ein Protokoll enthält Datumswerte, Fehler und zwei Arten einfacher Objekte. instanceof wählt das Date und den Error (ein RangeError ist auch ein Error). Einfache Objekte haben keine Klasse, deshalb ist isClick ein Type Predicate; danach bleibt nur KeyPress übrig. Dasselbe Predicate gibt log.filter(isClick) den Typ Click[]. Führen Sie npx tsc und dann node main.ts aus. Ändern Sie danach den Rückgabetyp von isClick in boolean und führen Sie tsc erneut aus.
main.ts
// An event log: instanceof for built-in classes, a type predicate for plain objects.
type Click = { x: number; y: number };
type KeyPress = { key: string };
type LogEntry = Date | Error | Click | KeyPress;
function isClick(entry: LogEntry): entry is Click {
return "x" in entry && "y" in entry;
}
function describe(entry: LogEntry): string {
if (entry instanceof Date) return "time " + entry.toISOString().slice(11, 19);
if (entry instanceof Error) return "error " + entry.message;
if (isClick(entry)) return "click at " + entry.x + "," + entry.y;
return "key " + entry.key; // only KeyPress is left
}
const log: LogEntry[] = [
new Date(Date.UTC(2026, 9, 4, 9, 30, 0)),
{ x: 10, y: 20 },
{ key: "Enter" },
new RangeError("volume above 100"),
{ x: 4, y: 2 },
];
for (const entry of log) {
console.log(describe(entry));
}
const clicks = log.filter(isClick); // Click[]: the predicate narrows the array too
console.log(clicks.length + " clicks, x values: " + clicks.map((c) => c.x).join(", "));
Ausführen mit
npx tsc
node main.tsAusgabe
time 09:30:00
click at 10,20
key Enter
error volume above 100
click at 4,2
2 clicks, x values: 10, 4- Mit boolean statt entry is Click werden entry.x und entry.key abgelehnt, ebenso c.x in der letzten Zeile.
- instanceof Error trifft auch den RangeError, weil RangeError Error erweitert.
- Das Date wird mit toISOString in UTC ausgegeben, deshalb ist die Ausgabe auf jedem Rechner gleich.
- filter(isClick) behält die Elemente, für die isClick true liefert, und das Predicate sagt tsc, was sie sind.
Ändern und ausführen
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser den TypeScript-Compiler herunter (bis zu 2.1 MB) und speichert ihn im Cache. Ihr Code bleibt auf Ihrem Gerät.
Übungen
Übung 1 von 2
Beschreiben, was geworfen wurde
errorText bekommt den Wert, den eine catch-Klausel erhalten hat, sein Typ ist also unknown. tsc meldet error TS18046: 'error' is of type 'unknown'. Liefern Sie für einen Error (jeder Art) die message, für einen geworfenen String den Text selbst und für alles andere "unknown error".
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser den TypeScript-Compiler herunter (bis zu 2.1 MB) und speichert ihn im Cache. Ihr Code bleibt auf Ihrem Gerät.
Hinweise
Hinweis 1
unknown erlaubt nichts, bis Sie eingrenzen. Welche Prüfung sagt Ihnen, dass ein Wert ein Error ist?
Hinweis 2
error instanceof Error ist auch für einen TypeError wahr, weil TypeError Error erweitert.
Hinweis 3
Ergänzen Sie nach der instanceof-Prüfung typeof error === "string" für geworfenen Text und liefern Sie am Ende "unknown error".
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
export function errorText(error: unknown): string {
if (error instanceof Error) {
return error.message;
}
if (typeof error === "string") {
return error;
}
return "unknown error";
}
Auf dem eigenen Computer ausführen
Installieren Sie TypeScript 7.0 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.ts
export function errorText(error: unknown): string {
return error.message;
}
main.test.ts
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {errorText} from './main.ts';
test('ein Error liefert seine message', () => {
assert.equal(errorText(new Error('disk full')), 'disk full', 'errorText(new Error("disk full")) sollte "disk full" sein');
});
test('ein TypeError liefert ebenfalls seine message', () => {
assert.equal(errorText(new TypeError('bad input')), 'bad input', 'errorText(new TypeError("bad input")) sollte "bad input" sein');
});
test('ein geworfener String kommt unverändert zurück', () => {
assert.equal(errorText('timeout'), 'timeout', 'errorText("timeout") sollte "timeout" sein');
});
test('alles andere ergibt "unknown error"', () => {
assert.equal(errorText(42), 'unknown error', 'errorText(42) sollte "unknown error" sein');
assert.equal(errorText(null), 'unknown error', 'errorText(null) sollte "unknown error" sein');
});
package.json
{
"type": "module"
}
tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "nodenext",
"lib": [
"esnext",
"dom"
],
"types": [],
"strict": true,
"noEmit": true,
"erasableSyntaxOnly": true,
"verbatimModuleSyntax": true,
"allowImportingTsExtensions": true
},
"include": [
"**/*.ts"
],
"exclude": [
"**/*.test.ts"
]
}
npx tsc braucht TypeScript im Ordner: Führen Sie dort einmal npm install --save-dev typescript aus. tsc prüft die Typen; Node.js führt main.ts aus, indem es sie entfernt.
Programm ausführen:
npx tsc
node main.tsPrüfungen ausführen (learnrun.js muss im selben Ordner liegen):
npx tsc
node --testlearnrun.js herunterladenÜbung 2 von 2
Die Seiten der Bücher zählen
Ein Regal enthält Bücher und Filme. totalPages filtert die Bücher mit isBook, aber tsc meldet error TS2339: Property 'pages' does not exist on type 'Item', weil isBook nur sagt, dass es einen Boolean liefert. Ändern Sie eine Sache, damit filter(isBook) Book[] liefert und die Datei die Typprüfung besteht.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser den TypeScript-Compiler herunter (bis zu 2.1 MB) und speichert ihn im Cache. Ihr Code bleibt auf Ihrem Gerät.
Hinweise
Hinweis 1
Der Rumpf von isBook ist in Ordnung. Das Problem ist, was sein Rückgabetyp tsc sagt.
Hinweis 2
Ein Type Predicate hat die Form parameterName is Type, mit dem Parameter dieser Funktion.
Hinweis 3
export function isBook(item: Item): item is Book {
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
export type Book = { title: string; pages: number };
export type Film = { title: string; minutes: number };
export type Item = Book | Film;
export function isBook(item: Item): item is Book {
return "pages" in item;
}
export function totalPages(items: Item[]): number {
let total = 0;
for (const book of items.filter(isBook)) {
total += book.pages;
}
return total;
}
Auf dem eigenen Computer ausführen
Installieren Sie TypeScript 7.0 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.ts
export type Book = { title: string; pages: number };
export type Film = { title: string; minutes: number };
export type Item = Book | Film;
export function isBook(item: Item): boolean {
return "pages" in item;
}
export function totalPages(items: Item[]): number {
let total = 0;
for (const book of items.filter(isBook)) {
total += book.pages;
}
return total;
}
main.test.ts
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {isBook, totalPages} from './main.ts';
const book = {title: 'Dune', pages: 412};
const film = {title: 'Alien', minutes: 117};
test('isBook erkennt ein Buch und einen Film', () => {
assert.equal(isBook(book), true, 'isBook(book) sollte true sein');
assert.equal(isBook(film), false, 'isBook(film) sollte false sein');
});
test('totalPages addiert nur die Seiten der Bücher', () => {
const shelf = [book, film, {title: 'Emma', pages: 474}];
assert.equal(totalPages(shelf), 886, 'totalPages sollte 412 + 474 = 886 sein');
});
test('totalPages eines Regals ohne Bücher ist 0', () => {
assert.equal(totalPages([film]), 0, 'totalPages([film]) sollte 0 sein');
});
package.json
{
"type": "module"
}
tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "nodenext",
"lib": [
"esnext",
"dom"
],
"types": [],
"strict": true,
"noEmit": true,
"erasableSyntaxOnly": true,
"verbatimModuleSyntax": true,
"allowImportingTsExtensions": true
},
"include": [
"**/*.ts"
],
"exclude": [
"**/*.test.ts"
]
}
npx tsc braucht TypeScript im Ordner: Führen Sie dort einmal npm install --save-dev typescript aus. tsc prüft die Typen; Node.js führt main.ts aus, indem es sie entfernt.
Programm ausführen:
npx tsc
node main.tsPrüfungen ausführen (learnrun.js muss im selben Ordner liegen):
npx tsc
node --testlearnrun.js herunterladenHäufige Fehler
.message vom gefangenen Wert lesen
try {
JSON.parse("{oops");
} catch (e) {
console.log("Could not read the settings: " + e.message);
}
Was tsc oder Node.js ausgibt
main.ts(4,49): error TS18046: 'e' is of type 'unknown'.Warum, und die Lösung
Geworfen werden kann alles, nicht nur Fehler, deshalb ist die catch-Variable mit strict unknown. Grenzen Sie sie zuerst ein: if (e instanceof Error) { console.log("Could not read the settings: " + e.message); }. Andere Werte behandeln Sie in einem else-Zweig oder machen sie mit String(e) zu Text.
Ein Type Guard, der nur boolean liefert
type Fish = { swim: () => void };
type Bird = { fly: () => void };
type Pet = Fish | Bird;
function isFish(pet: Pet): boolean {
return "swim" in pet;
}
function move(pet: Pet) {
if (isFish(pet)) {
pet.swim();
}
}
move({ swim: () => console.log("swimming") });
Was tsc oder Node.js ausgibt
main.ts(11,9): error TS2339: Property 'swim' does not exist on type 'Pet'.Warum, und die Lösung
Die Prüfung in isFish grenzt nur innerhalb von isFish ein. Für den Aufrufer sagt boolean nichts über pet, pet bleibt also ein Pet. Deklarieren Sie den Rückgabetyp als Type Predicate: function isFish(pet: Pet): pet is Fish. Dann grenzt if (isFish(pet)) pet auf Fish ein und den else-Zweig auf Bird.
instanceof mit einem primitiven Typnamen
function shout(value: unknown): string {
if (value instanceof string) {
return value.toUpperCase();
}
return "not text";
}
console.log(shout("hi"));
Was tsc oder Node.js ausgibt
main.ts(2,24): error TS2693: 'string' only refers to a type, but is being used as a value here.Warum, und die Lösung
Rechts von instanceof muss eine Klasse stehen, die zur Laufzeit existiert. string ist nur ein Typ; Node.js bräche mit ReferenceError: string is not defined ab. Für primitive Werte gibt es keine Klasse zum Prüfen: Nehmen Sie typeof value === "string". instanceof String würde kompilieren, ist für "hi" aber falsch, denn das ist ein primitiver Wert und kein String-Objekt.
TypeScript im Browser: der Compiler von TypeScript 6.0.3, Apache-2.0, danach die eigene Engine Ihres Browsers. Lizenz und Quellcode
Abschlussquiz
5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.
Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.