Aufwärmen · Aufgabe 1 von 7
Aufwärmen mit JavaScript: ein Objekt als Wörterbuch. Was gibt das aus?
const stock = {};
stock["apples"] = 3;
stock.pears = 2;
console.log(stock.apples + stock["pears"], stock.plums);// B5.2 · ca. 30 Min. · Einstieg
Nach dieser Lektion typisieren Sie Objekte mit vorab unbekannten Schlüsseln, kombinieren sie mit benannten Eigenschaften und behandeln fehlende Schlüssel.
Danach können Sie
Aufwärmen · Aufgabe 1 von 7
const stock = {};
stock["apples"] = 3;
stock.pears = 2;
console.log(stock.apples + stock["pears"], stock.plums);Vorhersagen · Aufgabe 2 von 7
const prices = { apple: 0.5, pear: 0.75 };
const item: string = "apple";
console.log(prices[item]);Üben · Aufgabe 3 von 7
interface Scores {
[name: ____]: number;
}
const scores: Scores = { Ada: 91, Grace: 78 };
scores.Linus = 85;
console.log(scores.Ada + scores.Grace + scores.Linus);Üben · Aufgabe 4 von 7
interface NumberOrStringDictionary {
____
length: number;
name: string;
}Üben · Aufgabe 5 von 7
interface ReadonlyStringArray {
readonly [index: number]: string;
}
const myArray: ReadonlyStringArray = ["Alice", "Bob"];
console.log(myArray[0]);
myArray[2] = "Mallory";Denksport · Aufgabe 6 von 7
interface Scores {
[name: string]: number;
}
const scores: Scores = { Ada: 91, Grace: 78 };
const missing = scores["Zoe"];
console.log(missing);
console.log(missing.toFixed(1));Anwenden · Aufgabe 7 von 7
Prüfen Sie Ihr Ergebnis anhand dieser Liste
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
Scores ordnet jedem Schülernamen eine Zahl zu. ClassInfo hat neben seiner Index-Signatur zwei benannte Eigenschaften, also ist der Index-Typ eine Union, zu der beide passen. GradeLimits ist readonly. Object.entries geht die Schlüssel durch, und scoreOf prüft mit in, bevor es einen möglicherweise fehlenden Schlüssel liest. Führen Sie npx tsc und dann node main.ts aus. Fügen Sie danach limits.D = 50; oder eine benannte Eigenschaft active: boolean in ClassInfo hinzu und führen Sie tsc erneut aus.
main.ts
// Scores per student: the names are not known when the type is written.
interface Scores {
[student: string]: number;
}
// A named property must fit the index type, so the index type is a union.
interface ClassInfo {
[key: string]: string | number;
name: string;
size: number;
}
// readonly: the grade limits can be read, not changed.
interface GradeLimits {
readonly [grade: string]: number;
}
const scores: Scores = { Ada: 91, Grace: 78 };
scores["Linus"] = 64;
const info: ClassInfo = { name: "7b", size: 3, room: "B12" };
const limits: GradeLimits = { A: 90, B: 75, C: 60 };
function gradeOf(score: number): string {
for (const [grade, min] of Object.entries(limits)) {
if (score >= min) return grade;
}
return "F";
}
for (const [student, score] of Object.entries(scores)) {
console.log(student + ": " + score + " (" + gradeOf(score) + ")");
}
// A key nobody set is undefined at run time, whatever the type says: check first.
function scoreOf(student: string): string {
if (!(student in scores)) return student + ": no score";
return student + ": " + scores[student];
}
console.log(scoreOf("Zoe"));
console.log("class " + info.name + " in room " + info.room);
Ausführen mit
npx tsc
node main.tsAusgabe
Ada: 91 (A)
Grace: 78 (B)
Linus: 64 (C)
Zoe: no score
class 7b in room B12Tab 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.
Übung 1 von 2
countVotes zählt, wie oft jede Option gewählt wurde, aber counts = {} hat keine Schlüssel, also meldet tsc error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'. Exportieren Sie ein Interface VoteCounts mit einer String-Index-Signatur, deren Werte Zahlen sind. Annotieren Sie counts damit, und geben Sie countVotes den Rückgabetyp VoteCounts. Führen Sie die Tests aus: Der erste ist die Typprüfung.
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.
Eine Index-Signatur steht im Interface: [option: string]: number;
Schreiben Sie export interface VoteCounts { … } über die Funktion, dann const counts: VoteCounts = {};
Der Rückgabetyp steht hinter der Parameterliste: countVotes(votes: string[]): VoteCounts {
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
export interface VoteCounts {
[option: string]: number;
}
export function countVotes(votes: string[]): VoteCounts {
const counts: VoteCounts = {};
for (const vote of votes) {
counts[vote] = (counts[vote] ?? 0) + 1;
}
return counts;
}
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 countVotes(votes: string[]) {
const counts = {};
for (const vote of votes) {
counts[vote] = (counts[vote] ?? 0) + 1;
}
return counts;
}
main.test.ts
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {countVotes} from './main.ts';
test('jede Option wird gezählt', () => {
const got = countVotes(['tea', 'coffee', 'tea']);
assert.deepEqual(got, {tea: 2, coffee: 1}, `countVotes ergab ${JSON.stringify(got)}`);
});
test('keine Stimmen ergeben ein leeres Objekt', () => {
const got = countVotes([]);
assert.deepEqual(got, {}, `countVotes([]) ergab ${JSON.stringify(got)}`);
});
test('eine Option ohne Stimme fehlt', () => {
const got = countVotes(['tea']);
assert.equal(got['water'], undefined, `water sollte fehlen, ist aber ${got['water']}`);
});
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
Messages enthält Texte nach Schlüssel und dazu eine Versionsnummer. tsc meldet error TS2411: Property 'version' of type 'number' is not assignable to 'string' index type 'string'., und das // @ts-expect-error in noEdits ist unbenutzt, weil sich messages noch ändern lässt. Machen Sie den Index-Typ zu einer Union, zu der version passt, und machen Sie die Index-Signatur readonly. Korrigieren Sie dann translate: Es gibt den Text in Großbuchstaben zurück oder den Schlüssel in eckigen Klammern, etwa [missing], wenn es zu dem Schlüssel keinen Text gibt, auch für version.
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.
Der Index-Typ muss Texte und die Version erlauben: readonly [key: string]: string | number;
messages[key] ist dann string | number und kann zur Laufzeit undefined sein. typeof text !== "string" fängt eine Zahl und undefined auf einmal ab.
const text = messages[key]; if (typeof text !== "string") return "[" + key + "]"; return text.toUpperCase();
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
export interface Messages {
readonly [key: string]: string | number;
version: number;
}
export const en: Messages = { version: 2, greeting: "Hello", farewell: "Goodbye" };
export function translate(messages: Messages, key: string): string {
const text = messages[key];
if (typeof text !== "string") return "[" + key + "]";
return text.toUpperCase();
}
function noEdits(messages: Messages) {
// @ts-expect-error: messages are read-only
messages["greeting"] = "Hi";
}
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 interface Messages {
[key: string]: string;
version: number;
}
export const en: Messages = { version: 2, greeting: "Hello", farewell: "Goodbye" };
export function translate(messages: Messages, key: string): string {
return messages[key].toUpperCase();
}
function noEdits(messages: Messages) {
// @ts-expect-error: messages are read-only
messages["greeting"] = "Hi";
}
main.test.ts
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {en, translate} from './main.ts';
test('ein bekannter Schlüssel ergibt den Text in Großbuchstaben', () => {
const got = translate(en, 'greeting');
assert.equal(got, 'HELLO', `translate(en, 'greeting') ergab ${JSON.stringify(got)}`);
});
test('ein fehlender Schlüssel ergibt den Schlüssel in Klammern', () => {
const got = translate(en, 'missing');
assert.equal(got, '[missing]', `translate(en, 'missing') ergab ${JSON.stringify(got)}`);
});
test('version ist kein Text', () => {
const got = translate(en, 'version');
assert.equal(got, '[version]', `translate(en, 'version') ergab ${JSON.stringify(got)}`);
});
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 herunterladeninterface Stock {
[product: string]: number;
updated: string;
}
Was tsc oder Node.js ausgibt
main.ts(3,3): error TS2411: Property 'updated' of type 'string' is not assignable to 'string' index type 'number'.Warum, und die Lösung
Die Index-Signatur sagt, jeder Schlüssel liefert eine Zahl, und updated ist auch ein Schlüssel: stock["updated"] muss eine Zahl sein. Erweitern Sie entweder den Index-Typ, [product: string]: number | string, dann liest sich jedes Produkt als number | string, oder legen Sie die Mengen in eine eigene Eigenschaft: { updated: string; counts: { [product: string]: number } }.
const prices = { apple: 0.5, pear: 0.75 };
const item: string = "apple";
console.log(prices[item]);
Was tsc oder Node.js ausgibt
main.ts(3,13): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ apple: number; pear: number; }'.Warum, und die Lösung
prices hat zwei bekannte Schlüssel, und item kann ein beliebiger String sein, also kann tsc nicht sagen, was prices[item] ist. Sind die Schlüssel offen, sagen Sie es: const prices: { [item: string]: number } = { … }, und prüfen Sie auf fehlende Schlüssel. Ist item immer apple oder pear, geben Sie ihm diesen Typ: const item: "apple" | "pear" = "apple".
interface Limits {
readonly [grade: string]: number;
}
const limits: Limits = { A: 90, B: 75 };
console.log(limits.A);
limits.C = 60;
Was tsc oder Node.js ausgibt
main.ts(7,1): error TS2542: Index signature in type 'Limits' only permits reading.Warum, und die Lösung
readonly gilt für jeden Schlüssel, den die Signatur abdeckt, auch für noch nicht vorhandene: limits.C = 60 fügt einen hinzu, und das ist ein Schreibzugriff. Geben Sie C seinen Wert beim Erzeugen des Objekts, { A: 90, B: 75, C: 60 }, oder erzeugen Sie ein neues Objekt: const more: Limits = { ...limits, C: 60 }.
TypeScript im Browser: der Compiler von TypeScript 6.0.3, Apache-2.0, danach die eigene Engine Ihres Browsers. Lizenz und Quellcode
5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.
Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.