Aufwärmen · Aufgabe 1 von 7
// B3.2 · ca. 30 Min. · Einstieg
typeof, Truthiness, Gleichheit und in
Nach dieser Lektion grenzen Sie eine Union mit typeof, einer Truthiness-Prüfung, einem Gleichheitsvergleich oder in ein und wissen, welche Werte jede Prüfung durchlässt oder verwirft.
Danach können Sie
- Mit typeof eingrenzen und dabei seine acht Ergebnis-Strings kennen und wissen, dass "object" auch null und Arrays umfasst
- Truthiness-Prüfungen und ?? einsetzen und erkennen, wo eine Truthiness-Prüfung 0 oder den leeren String fälschlich verwirft
- Mit ===, == null, switch und dem in-Operator eingrenzen, auch bei optionalen Eigenschaften
Vorhersagen · Aufgabe 2 von 7
Sagen Sie es vorher, bevor Sie weiterlesen: tsc meldet für diese Datei keine Fehler. Was gibt node main.ts aus?
function scoreLabel(score: number | null): string { if (score) { return "Score: " + score; } return "Not played yet"; } console.log(scoreLabel(0));Üben · Aufgabe 3 von 7
Setzen Sie den String ein, damit tsc die Funktion annimmt und das Programm 1.50 EUR ausgibt.
function format(value: string | number): string { if (typeof value === "____") { return value.toFixed(2); } return value.toUpperCase(); } console.log(format(1.5), format("eur"));if (typeof value === "") {Üben · Aufgabe 4 von 7
v hat den Typ Setting unten. Ordnen Sie jeder Prüfung den Typ zu, den v in ihrem if-Block hat.
type Setting = string | number | string[] | null | undefined;Üben · Aufgabe 5 von 7
Welche Zeile lehnt tsc ab?
function show(v: string | string[] | Date): string { if (typeof v === "object") { return v.join(", "); } return v.toUpperCase(); }Denksport · Aufgabe 6 von 7
Knobelaufgabe. Ein Mensch kann mit der richtigen Ausrüstung schwimmen oder fliegen, also ist beides optional. Welchen Typ hat animal im if?
type Fish = { swim: () => void }; type Bird = { fly: () => void }; type Human = { swim?: () => void; fly?: () => void }; function move(animal: Fish | Bird | Human) { if ("swim" in animal) { // what is animal here? } }Anwenden · Aufgabe 7 von 7
Kleine Aufgabe. Schreiben Sie describeInput(value: Input) für type Input = string | number | string[] | null | undefined. Die Funktion liefert "nothing" für null und undefined, "N items" für ein Array, "the number N" für eine Zahl, "empty text" für "" und "text …" für jeden anderen String. Achten Sie darauf, dass 0 und "" eigene Antworten bekommen, und geben Sie vier Aufrufe mit console.log aus. 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 Einstellungsbericht
show macht aus jeder Art von Einstellung Text. == null entfernt null und undefined mit einer Prüfung, dann wählt typeof string, number und boolean, und für join bleibt nur string[]. Achten Sie auf die Reihenfolge: 0, false und "" erreichen ihre eigenen Zweige, weil die erste Prüfung == null ist und keine Truthiness-Prüfung. connection unterscheidet mit in ein Gerät mit Kabel von einem mit WLAN; password ist optional und braucht deshalb noch eine Prüfung. Führen Sie npx tsc und dann node main.ts aus. Ändern Sie danach die erste Zeile von show in if (!value) und vergleichen Sie die Ausgabe.
main.ts
// A settings report. Each check narrows value for the code it guards.
type Setting = string | number | boolean | string[] | null | undefined;
function show(value: Setting): string {
if (value == null) return "(not set)"; // == null removes null and undefined
if (typeof value === "string") return '"' + value + '"';
if (typeof value === "number") return value.toFixed(1);
if (typeof value === "boolean") return value ? "on" : "off";
return value.join(", "); // only string[] is left
}
type Wired = { port: number; cable: string };
type Wireless = { port: number; network: string; password?: string };
function connection(device: Wired | Wireless): string {
if ("network" in device) {
// here device is Wireless; password may still be missing
return "wifi " + device.network + (device.password ? " (locked)" : " (open)");
}
return "cable " + device.cable; // here device is Wired
}
const settings: Setting[] = ["dark", 0, false, ["de", "en"], "", null, undefined];
for (const value of settings) {
console.log(show(value));
}
console.log(connection({ port: 1, cable: "LAN" }));
console.log(connection({ port: 2, network: "home", password: "secret" }));
console.log(connection({ port: 3, network: "cafe" }));
Ausführen mit
npx tsc
node main.tsAusgabe
"dark"
0.0
off
de, en
""
(not set)
(not set)
cable LAN
wifi home (locked)
wifi cafe (open)- Mit if (!value) als erster Prüfung würden 0, false und "" alle (not set) ausgeben, und tsc würde trotzdem keine Fehler melden.
- Nachdem die drei typeof-Prüfungen zurückgekehrt sind, kann value nur noch ein string[] sein, also kompiliert value.join ohne Prüfung.
- Im wahren Zweig von "network" in device ist device Wireless, im Rest der Funktion Wired.
- device.password ist optional, die in-Prüfung macht es also nicht definiert; der Ausdruck mit ? : prüft es.
Ä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
Die Null behalten
stockLabel soll nur dann "unknown" sagen, wenn der Bestand null oder undefined ist, und startVolume soll nur dann auf 50 zurückfallen, wenn nichts gespeichert wurde. Beide nutzen eine Truthiness-Prüfung, deshalb kommen ein Bestand von 0 und eine stummgeschaltete Lautstärke von 0 falsch heraus. tsc meldet nichts: Die Typen stimmen. Korrigieren Sie die beiden Prüfungen, sodass die Tests bestehen.
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
Für welche Werte ist !count wahr? 0 gehört dazu, aber 0 ist ein echter Bestand.
Hinweis 2
count == null ist genau für null und undefined wahr und für nichts sonst.
Hinweis 3
saved || 50 fällt bei jedem falsy Wert zurück. Der Operator, der nur bei null und undefined zurückfällt, ist ??.
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
export function stockLabel(count: number | null | undefined): string {
if (count == null) {
return "unknown";
}
return count + " in stock";
}
export function startVolume(saved: number | undefined): number {
return saved ?? 50;
}
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 stockLabel(count: number | null | undefined): string {
if (!count) {
return "unknown";
}
return count + " in stock";
}
export function startVolume(saved: number | undefined): number {
return saved || 50;
}
main.test.ts
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {stockLabel, startVolume} from './main.ts';
test('stockLabel(5) ist "5 in stock"', () => {
assert.equal(stockLabel(5), '5 in stock', 'stockLabel(5) sollte "5 in stock" sein');
});
test('stockLabel(0) ist "0 in stock"', () => {
assert.equal(stockLabel(0), '0 in stock', 'stockLabel(0) sollte "0 in stock" sein: 0 ist ein echter Bestand');
});
test('stockLabel(null) und stockLabel(undefined) sind "unknown"', () => {
assert.equal(stockLabel(null), 'unknown', 'stockLabel(null) sollte "unknown" sein');
assert.equal(stockLabel(undefined), 'unknown', 'stockLabel(undefined) sollte "unknown" sein');
});
test('startVolume(undefined) ist 50', () => {
assert.equal(startVolume(undefined), 50, 'startVolume(undefined) sollte auf 50 zurückfallen');
});
test('startVolume(0) ist 0', () => {
assert.equal(startVolume(0), 0, 'startVolume(0) sollte 0 bleiben: gespeichert wurde "stumm"');
});
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
Einen Kontakt erreichen
Ein Contact hat entweder eine E-Mail oder ein Telefon, und ein Telefon kann als Text oder als Zahl gespeichert sein. tsc meldet error TS2339: Property 'email' does not exist on type 'Contact'. Sorgen Sie dafür, dass reach "write to <email>" für eine E-Mail liefert, "call <phone>" für ein Telefon als Text und "call +<phone>" für ein Telefon als Zahl.
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
Nur Email deklariert email, deshalb ist contact.email auf einem Contact nicht erlaubt. "email" in contact grenzt contact auf Email ein.
Hinweis 2
Nachdem dieses if zurückgekehrt ist, ist contact ein Phone. typeof contact.phone === "number" grenzt dann die Eigenschaft ein.
Hinweis 3
Drei returns: "write to " + contact.email, "call +" + contact.phone für eine Zahl und "call " + contact.phone für Text.
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
export type Email = { email: string };
export type Phone = { phone: string | number };
export type Contact = Email | Phone;
export function reach(contact: Contact): string {
if ("email" in contact) {
return "write to " + contact.email;
}
if (typeof contact.phone === "number") {
return "call +" + contact.phone;
}
return "call " + contact.phone;
}
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 Email = { email: string };
export type Phone = { phone: string | number };
export type Contact = Email | Phone;
export function reach(contact: Contact): string {
return "write to " + contact.email;
}
main.test.ts
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {reach} from './main.ts';
test('ein E-Mail-Kontakt ergibt "write to …"', () => {
assert.equal(reach({email: 'ada@example.com'}), 'write to ada@example.com', 'reach({email: "ada@example.com"}) sollte "write to ada@example.com" sein');
});
test('ein Telefon als Text ergibt "call …"', () => {
assert.equal(reach({phone: '+49 30 1234567'}), 'call +49 30 1234567', 'reach({phone: "+49 30 1234567"}) sollte "call +49 30 1234567" sein');
});
test('ein Telefon als Zahl ergibt "call +…"', () => {
assert.equal(reach({phone: 49301234567}), 'call +49301234567', 'reach({phone: 49301234567}) sollte "call +49301234567" 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
in auf einem Wert, der ein String sein kann
type Tags = string | string[];
function count(tags: Tags): number {
if ("length" in tags) {
return tags.length;
}
return 1;
}
console.log(count(["a", "b"]));
Was tsc oder Node.js ausgibt
main.ts(4,19): error TS2322: Type 'Tags' is not assignable to type 'object'.Warum, und die Lösung
in funktioniert nur mit Objekten. tags kann ein String sein, und zur Laufzeit bräche count("news") mit TypeError: Cannot use 'in' operator to search for 'length' in news ab. Seit TypeScript 4.9 prüft tsc, dass rechts von in ein Objekt steht. Wählen Sie die Mitglieder mit einer Prüfung, die für jeden Wert funktioniert: if (Array.isArray(tags)) für das Array oder typeof tags === "string" für den Text.
undefined entfernt, null vergessen
function initial(name: string | null | undefined): string {
if (name !== undefined) {
return name.charAt(0);
}
return "?";
}
console.log(initial("Ada"));
Was tsc oder Node.js ausgibt
main.ts(3,12): error TS18047: 'name' is possibly 'null'.Warum, und die Lösung
!== undefined entfernt nur undefined; im if ist null weiterhin möglich. Nutzen Sie die lockere Prüfung name != null, die null und undefined zusammen entfernt, oder name !== null && name !== undefined. Eine Truthiness-Prüfung, if (name), würde auch kompilieren, schickt aber auch den leeren String zu "?"; das ist nur in Ordnung, wenn Sie genau das wollen.
Eine optionale Methode nach einer in-Prüfung aufrufen
type Fish = { swim: () => void };
type Human = { name: string; swim?: () => void };
function dive(animal: Fish | Human) {
if ("swim" in animal) {
animal.swim();
}
}
dive({ swim: () => console.log("splash") });
Was tsc oder Node.js ausgibt
main.ts(6,5): error TS2722: Cannot invoke an object which is possibly 'undefined'.Warum, und die Lösung
Human deklariert swim als optional, deshalb bleibt Human im wahren Zweig der in-Prüfung, und dort kann swim undefined sein. Die in-Prüfung sagt Ihnen, dass die Eigenschaft deklariert ist, nicht, dass sie eine Funktion enthält. Prüfen Sie den Wert selbst: if (animal.swim) { animal.swim(); }, oder rufen Sie sie mit animal.swim?.() auf.
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.