Aufwärmen · Aufgabe 1 von 7
Aufwärmen mit JavaScript-Destructuring: Welche Defaults werden verwendet? Was gibt das aus?
const { a = 1, b = 2, c = 3 } = { a: undefined, b: null };
console.log(a, b, c);// B5.1 · ca. 30 Min. · Einstieg
Nach dieser Lektion geben Sie optionalen Eigenschaften Defaults dort, wo Sie sie destrukturieren, machen Eigenschaften readonly und können sagen, was readonly schützt und was nicht.
Lektion 1 von 5 in B5 Objekttypen
Danach können Sie
Aufwärmen · Aufgabe 1 von 7
const { a = 1, b = 2, c = 3 } = { a: undefined, b: null };
console.log(a, b, c);Vorhersagen · Aufgabe 2 von 7
interface Home {
readonly resident: { name: string; age: number };
}
function visitForBirthday(home: Home) {
home.resident.age++;
}
function evict(home: Home) {
home.resident = { name: "Victor the Evictor", age: 42 };
}Üben · Aufgabe 3 von 7
interface PaintOptions {
shape: string;
xPos?: number;
yPos?: number;
}
function paintShape({ shape, xPos ____, yPos = 0 }: PaintOptions) {
console.log(shape + " at " + xPos + "," + yPos);
}
paintShape({ shape: "circle" });
paintShape({ shape: "square", xPos: 100 });Üben · Aufgabe 4 von 7
interface Settings {
theme?: string;
fontSize?: number;
}
function describe(settings: Settings) {
const { theme, fontSize = 14 } = settings;
// here
}Üben · Aufgabe 5 von 7
interface UserDefaults {
colorThemeOverride?: "dark" | "light";
}
const settings: UserDefaults = {};
settings.colorThemeOverride = "dark";
settings.colorThemeOverride = undefined;
delete settings.colorThemeOverride;Denksport · Aufgabe 6 von 7
interface Person {
name: string;
age: number;
}
interface ReadonlyPerson {
readonly name: string;
readonly age: number;
}
let writablePerson: Person = { name: "Person McPersonface", age: 42 };
let readonlyPerson: ReadonlyPerson = writablePerson;
console.log(readonlyPerson.age);
writablePerson.age++;
console.log(readonlyPerson.age);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
describeOrder destrukturiert seinen Parameter und gibt quantity und giftWrap Defaults, sodass der Rumpf keine Prüfungen auf undefined braucht. id und customer sind readonly. Danach ändert das Programm die Stadt des Kunden, was readonly erlaubt, weil es nur eine Ebene tief wirkt. Führen Sie npx tsc und dann node main.ts aus. Fügen Sie danach order.id = "X"; oder order.customer = order.customer; hinzu und führen Sie tsc erneut aus.
main.ts
// An order: optional settings get defaults, the id and the customer are readonly.
interface Customer {
name: string;
city: string;
}
interface Order {
readonly id: string;
readonly customer: Customer;
quantity?: number;
giftWrap?: boolean;
}
// Defaults in the pattern: in the body, quantity is a number and giftWrap a boolean.
function describeOrder({ id, customer, quantity = 1, giftWrap = false }: Order): string {
const wrap = giftWrap ? ", gift-wrapped" : "";
return id + ": " + quantity + " for " + customer.name + " in " + customer.city + wrap;
}
const order: Order = { id: "A-17", customer: { name: "Ada", city: "Bonn" } };
console.log(describeOrder(order));
// readonly is shallow: order.customer cannot point elsewhere, but its city can change.
order.customer.city = "Berlin";
console.log(describeOrder({ ...order, quantity: 3, giftWrap: true }));
// An explicit undefined gets the default too.
console.log(describeOrder({ id: "B-2", customer: order.customer, quantity: undefined }));
Ausführen mit
npx tsc
node main.tsAusgabe
A-17: 1 for Ada in Bonn
A-17: 3 for Ada in Berlin, gift-wrapped
B-2: 1 for Ada in BerlinTab 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
formatPrice soll für { amount: 4.5 } 4.50 EUR liefern: currency hat den Default eur, in Großbuchstaben geschrieben, und decimals den Default 2. tsc meldet error TS18048: 'options.currency' is possibly 'undefined'. Destrukturieren Sie stattdessen den Parameter und geben Sie currency und decimals ihre Defaults im Muster. decimals: 0 muss 0 bleiben. 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.
Ersetzen Sie den Parameter options: PriceOptions durch ein Muster: ({ amount, currency, decimals }: PriceOptions).
Ein Default steht im Muster hinter dem Namen, mit =: currency = "eur".
Verwenden Sie die Variablen im Rumpf: amount.toFixed(decimals) + " " + currency.toUpperCase().
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
export interface PriceOptions {
amount: number;
currency?: string;
decimals?: number;
}
export function formatPrice({ amount, currency = "eur", decimals = 2 }: PriceOptions): string {
return amount.toFixed(decimals) + " " + currency.toUpperCase();
}
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 PriceOptions {
amount: number;
currency?: string;
decimals?: number;
}
export function formatPrice(options: PriceOptions): string {
return options.amount.toFixed(options.decimals) + " " + options.currency.toUpperCase();
}
main.test.ts
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {formatPrice} from './main.ts';
test('beide Defaults werden verwendet', () => {
const got = formatPrice({amount: 4.5});
assert.equal(got, '4.50 EUR', `formatPrice({amount: 4.5}) ergab ${JSON.stringify(got)}`);
});
test('eine angegebene currency und decimals werden verwendet', () => {
const got = formatPrice({amount: 3, currency: 'usd', decimals: 1});
assert.equal(got, '3.0 USD', `formatPrice({amount: 3, currency: 'usd', decimals: 1}) ergab ${JSON.stringify(got)}`);
});
test('decimals: 0 bleibt 0', () => {
const got = formatPrice({amount: 7, decimals: 0});
assert.equal(got, '7 EUR', `formatPrice({amount: 7, decimals: 0}) 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 herunterladenÜbung 2 von 2
Eine Bestellung muss ihre id und ihr Kundenobjekt behalten; andere Teile des Shops halten dasselbe Kundenobjekt und müssen eine neue Stadt sehen. forbiddenChanges prüft den ersten Teil: Jedes // @ts-expect-error erwartet einen Fehler in der nächsten Zeile, und tsc meldet error TS2578: Unused '@ts-expect-error' directive., solange die Zuweisungen noch kompilieren. Machen Sie id und customer readonly. Korrigieren Sie dann moveCustomer, das tsc jetzt ablehnt: Ändern Sie die Stadt des Kundenobjekts, das die Bestellung schon hat.
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.
readonly steht im Interface vor dem Eigenschaftsnamen: readonly id: string;
Danach ist order.customer = { … } in moveCustomer error TS2540. readonly ist flach: Die Stadt in customer kann sich weiterhin ändern.
Der Rumpf von moveCustomer wird zu order.customer.city = city;
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
export interface Order {
readonly id: string;
readonly customer: { name: string; city: string };
total: number;
}
export function moveCustomer(order: Order, city: string): void {
order.customer.city = city;
}
export function addToTotal(order: Order, amount: number): void {
order.total += amount;
}
function forbiddenChanges(order: Order) {
// @ts-expect-error: an order keeps its id
order.id = "X-0";
// @ts-expect-error: an order keeps its customer object
order.customer = { name: "Mallory", city: "Nowhere" };
}
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 Order {
id: string;
customer: { name: string; city: string };
total: number;
}
export function moveCustomer(order: Order, city: string): void {
order.customer = { name: order.customer.name, city };
}
export function addToTotal(order: Order, amount: number): void {
order.total += amount;
}
function forbiddenChanges(order: Order) {
// @ts-expect-error: an order keeps its id
order.id = "X-0";
// @ts-expect-error: an order keeps its customer object
order.customer = { name: "Mallory", city: "Nowhere" };
}
main.test.ts
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {moveCustomer, addToTotal} from './main.ts';
test('moveCustomer setzt die neue Stadt', () => {
const order = {id: 'A-1', customer: {name: 'Ada', city: 'Bonn'}, total: 10};
moveCustomer(order, 'Berlin');
assert.equal(order.customer.city, 'Berlin', `die Stadt ist ${order.customer.city}`);
});
test('moveCustomer behält dasselbe Kundenobjekt', () => {
const customer = {name: 'Ada', city: 'Bonn'};
const order = {id: 'A-1', customer, total: 10};
moveCustomer(order, 'Berlin');
assert.ok(order.customer === customer, 'order.customer wurde durch ein neues Objekt ersetzt');
assert.equal(customer.city, 'Berlin', `das gemeinsame Kundenobjekt sagt noch ${customer.city}`);
});
test('addToTotal addiert zur Summe', () => {
const order = {id: 'A-1', customer: {name: 'Ada', city: 'Bonn'}, total: 10};
addToTotal(order, 2.5);
assert.equal(order.total, 12.5, `die Summe ist ${order.total}`);
});
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 Config {
readonly apiUrl: string;
retries: number;
}
const config: Config = { apiUrl: "https://example.com", retries: 3 };
config.retries = 5;
config.apiUrl = "http://localhost";
Was tsc oder Node.js ausgibt
main.ts(8,8): error TS2540: Cannot assign to 'apiUrl' because it is a read-only property.Warum, und die Lösung
Der Typ sagt, dass apiUrl einmal beim Erzeugen des Objekts gesetzt und nie geändert wird. Brauchen Sie eine andere URL, erzeugen Sie ein neues Objekt: const local: Config = { ...config, apiUrl: "http://localhost" }. Muss sie sich wirklich an Ort und Stelle ändern, sollte die Eigenschaft nicht readonly sein: Entfernen Sie den Modifier, statt ihn zu umgehen.
interface Settings {
theme?: string;
fontSize?: number;
}
function describe(settings: Settings) {
const { theme, fontSize = 14 } = settings;
return theme.toUpperCase() + " " + fontSize + "px";
}
Was tsc oder Node.js ausgibt
main.ts(8,10): error TS18048: 'theme' is possibly 'undefined'.Warum, und die Lösung
Destructuring entfernt undefined nicht: theme ist string | undefined, wie settings.theme es wäre. fontSize ist nur deshalb eine Zahl, weil es einen Default hat. Geben Sie theme auch einen, const { theme = "light", fontSize = 14 } = settings, oder prüfen Sie es auf undefined, bevor Sie toUpperCase aufrufen.
interface Options {
host?: string;
port?: number;
}
function connect({ host = "localhost", port = 80 }: Options) {
return host + ":" + port;
}
console.log(connect());
Was tsc oder Node.js ausgibt
main.ts(10,13): error TS2554: Expected 1 arguments, but got 0.Warum, und die Lösung
Die Defaults gehören zu den Eigenschaften des Objekts, aber das Objekt selbst ist weiterhin ein Pflichtparameter, und undefined zu destrukturieren würde einen TypeError werfen. Geben Sie auch dem ganzen Parameter einen Default: function connect({ host = "localhost", port = 80 }: Options = {}). Dann verwendet connect() ein leeres Objekt, und beide Eigenschafts-Defaults greifen.
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.