Zum Inhalt springen
aviral gupta

// B4.5 · ca. 38 Min. · Einstieg

Funktionen zusammen; Projekt: eine typisierte Hilfsbibliothek

Nach dieser Lektion können Sie eine kleine Bibliothek typisierter Funktionen schreiben und testen, die alles aus diesem Modul nutzt: Callback-Typen, flexible Parameter, Overloads und einen Wächter, der nie zurückkehrt.

Lektion 5 von 5 in B4 Mehr zu Funktionen

Ende des Moduls

Danach können Sie

  • Callbacks mit benannten Funktionstyp-Ausdrücken typisieren und sagen, welche Callbacks dazu passen
  • Optionale, Default- und Rest-Parameter mit einem Satz Overloads in einer kleinen Bibliothek kombinieren
  • Eine Bibliothek mit einem Helfer vom Typ never absichern und ihre Exporte mit node:test testen
  1. Aufwärmen · Aufgabe 1 von 7

    Aufwärmen mit JavaScript: countWhere zählt die Werte, für die test ein truthy Ergebnis liefert. Was gibt das aus?

    function countWhere(values, test) {
      let count = 0;
      values.forEach((value, i) => {
        if (test(value, i)) count++;
      });
      return count;
    }
    
    console.log(countWhere([5, 0, 8], (v) => v), countWhere([5, 0, 8], (v, i) => i), countWhere([5, 0, 8], () => {}));
  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie es vorher, bevor Sie weiterlesen. fail wirft immer, hat aber keinen Rückgabetyp. Welche Zeile lehnt tsc ab?

    function fail(message: string) {
      throw new Error(message);
    }
    
    function required(value: number | undefined, name: string): number {
      if (value === undefined) fail(name + " is missing");
      return value;
    }
  3. Üben · Aufgabe 3 von 7

    Ergänzen Sie den Rückgabetyp von Predicate, sodass tsc beide Callbacks annimmt und das Programm 2 2 ausgibt.

    type Predicate = (value: number, index: number) => ____;
    
    function countWhere(values: number[], test: Predicate): number {
      let count = 0;
      values.forEach((value, i) => {
        if (test(value, i)) count++;
      });
      return count;
    }
    
    console.log(countWhere([72, 100, 64, 0], (s) => s >= 65), countWhere([5, 0, 8], (v, i) => i > 0));
    type Predicate = (value: number, index: number) => ;
  4. Üben · Aufgabe 4 von 7

    join soll als join(), join("-") und join("-", "a", "b") funktionieren. Mit welcher ersten Zeile kompilieren alle drei Aufrufe?

    function join(/* ? */) {
      return words.join(separator);
    }
    
    console.log(join(), join("-"), join("-", "a", "b"));
  5. Üben · Aufgabe 5 von 7

    last hat zwei Overloads. Welchen Typ hat b?

    function last(text: string): string;
    function last(values: number[]): number;
    function last(input: string | number[]): string | number {
      return input[input.length - 1];
    }
    
    const b = last([4, 2]);
    // what is b here?
  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. last wird mit einem leeren String aufgerufen. Was passiert, wenn Sie npx tsc und dann node main.ts ausführen?

    function last(text: string): string;
    function last(values: number[]): number;
    function last(input: string | number[]): string | number {
      return input[input.length - 1];
    }
    
    const letter = last("");
    console.log(typeof letter, letter === undefined);
  7. Anwenden · Aufgabe 7 von 7

    Mini-Aufgabe. Schreiben Sie eine kleine Bibliothek für Wörter. Einen Typ WordTest für einen Callback, der ein Wort nimmt und einen boolean zurückgibt, und firstWhere(list, test), das das erste passende Wort zurückgibt oder fail(message): never aufruft. Einen Typ WordVisitor, dessen Ergebnis ignoriert wird, und eachWord(list, visit). shorten(text, max = 5, ...marks), das text auf max Zeichen kürzt und die marks anhängt. Und einen Satz Overloads, dessen Rückgabetyp vom Argument abhängt, etwa double(value: number): number und double(value: string): string. Rufen Sie jede Funktion einmal auf. 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

Eine kleine Bibliothek von Zahlen-Helfern

Das ganze Modul in einer Datei. Predicate und Visitor sind die Callback-Typen; fail ist der Wächter, der nie zurückkehrt und den required und last nutzen; clamp hat Defaults und sum einen Rest-Parameter; last hat zwei Overloads. Die Aufrufe unten nutzen jeden Export einmal. Führen Sie npx tsc und dann node main.ts aus. Entfernen Sie dann : never bei fail und führen Sie npx tsc erneut aus.

main.ts

// A small typed utility library, then a few calls that use it.
export type Predicate = (value: number, index: number) => boolean;
export type Visitor = (value: number, index: number) => void;

export function fail(message: string): never {
  throw new Error(message);
}

export function required(value: number | undefined, name: string): number {
  if (value === undefined) fail(name + " is missing");
  return value; // a number here: fail never returns
}

export function clamp(value: number, min = 0, max = 100): number {
  return Math.min(Math.max(value, min), max);
}

export function sum(...values: number[]): number {
  let total = 0;
  for (const value of values) total += value;
  return total;
}

export function countWhere(values: number[], test: Predicate): number {
  let count = 0;
  values.forEach((value, i) => {
    if (test(value, i)) count++;
  });
  return count;
}

export function each(values: number[], visit: Visitor): void {
  values.forEach((value, i) => visit(value, i));
}

export function last(text: string): string;
export function last(values: number[]): number;
export function last(input: string | number[]): string | number {
  if (input.length === 0) fail("last() needs at least one element");
  return input[input.length - 1];
}

const scores = [72, 105, 64, -3];
const fixed = scores.map((s) => clamp(s));
console.log("clamped:", fixed.join(" "), "sum:", sum(...fixed));
console.log("passed:", countWhere(fixed, (s) => s >= 65));
const lines: string[] = [];
each(fixed, (s, i) => lines.push("#" + (i + 1) + "=" + s));
console.log(lines.join(", "));
console.log("last:", last("Ada"), last(fixed) + 1);
try {
  required(undefined, "width");
} catch (error) {
  console.log(error instanceof Error ? error.message : error);
}

Ausführen mit

npx tsc
node main.ts

Ausgabe

clamped: 72 100 64 0 sum: 236
passed: 2
#1=72, #2=100, #3=64, #4=0
last: a 1
width is missing
  • clamp(s) nutzt beide Defaults, also wird 105 zu 100 und -3 zu 0. sum(...fixed) verteilt das Array auf den Rest-Parameter.
  • Der Callback für each gibt die Zahl von push zurück, und der Typ Visitor, der void zurückgibt, nimmt ihn an.
  • last(fixed) ist nach dem zweiten Overload eine Zahl, also ist + 1 Arithmetik: 0 + 1.
  • Ohne : never wird fail als void inferiert, und return value in required ist error TS2322.
Ä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 3

Defaults, eine optionale Einheit und ein Rest-Parameter

Die drei Aufrufe unten passen nicht zu den Signaturen, also meldet tsc error TS2554 für clamp(104) und label(5). Ändern Sie die Signaturen, nicht die Aufrufe, außer total: clamp bekommt die Defaults min = 0 und max = 100; unit in label wird optional, und label(5) ergibt "5"; sum bekommt einen Rest-Parameter, und total wird zu sum(3, 4.5, 2).

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
  1. Hinweis 1

    Ein Default steht hinter dem Parameter: min = 0. Sein Typ, number, wird aus dem Default inferiert.

  2. Hinweis 2

    unit?: string macht unit optional. In label ist es string | undefined, also prüfen Sie unit === undefined und geben dann String(value) zurück.

  3. Hinweis 3

    Ein Rest-Parameter: ...values: number[]. Der Rumpf bleibt gleich, denn values ist weiterhin ein Array.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

export function clamp(value: number, min = 0, max = 100): number {
  return Math.min(Math.max(value, min), max);
}

export function sum(...values: number[]): number {
  let total = 0;
  for (const value of values) total += value;
  return total;
}

export function label(value: number, unit?: string): string {
  return unit === undefined ? String(value) : value + " " + unit;
}

export const percent = clamp(104);
export const total = sum(3, 4.5, 2);
export const size = label(5);
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 clamp(value: number, min: number, max: number): number {
  return Math.min(Math.max(value, min), max);
}

export function sum(values: number[]): number {
  let total = 0;
  for (const value of values) total += value;
  return total;
}

export function label(value: number, unit: string): string {
  return value + " " + unit;
}

export const percent = clamp(104);
export const total = sum([3, 4.5, 2]);
export const size = label(5);

main.test.ts

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {clamp, sum, label, percent, total, size} from './main.ts';

test('clamp nutzt 0 und 100 als Defaults', () => {
  assert.equal(clamp(150), 100, `clamp(150) gab ${clamp(150)} zurück`);
  assert.equal(clamp(-5), 0, `clamp(-5) gab ${clamp(-5)} zurück`);
  assert.equal(percent, 100, `percent ist ${percent}`);
});

test('clamp nimmt eigene Grenzen, und undefined behält einen Default', () => {
  assert.equal(clamp(15, 20), 20, `clamp(15, 20) gab ${clamp(15, 20)} zurück`);
  assert.equal(clamp(15, undefined, 10), 10, `clamp(15, undefined, 10) gab ${clamp(15, undefined, 10)} zurück`);
});

test('sum nimmt beliebig viele Zahlen', () => {
  assert.equal(sum(), 0, `sum() gab ${sum()} zurück`);
  assert.equal(sum(3, 4.5, 2), 9.5, `sum(3, 4.5, 2) gab ${sum(3, 4.5, 2)} zurück`);
  assert.equal(total, 9.5, `total ist ${total}`);
});

test('label lässt eine fehlende Einheit weg', () => {
  assert.equal(label(5), '5', `label(5) gab ${JSON.stringify(label(5))} zurück`);
  assert.equal(label(5, 'kg'), '5 kg', `label(5, 'kg') gab ${JSON.stringify(label(5, 'kg'))} zurück`);
  assert.equal(size, '5', `size ist ${JSON.stringify(size)}`);
});

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.ts

Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):

npx tsc
node --test
learnrun.js herunterladen

Übung 2 von 3

Callback-Typen statt Function

countWhere und each nehmen ihre Callbacks als Function, also haben die Parameter score und i der Aufrufer keinen Typ: tsc meldet error TS7006: Parameter 'score' implicitly has an 'any' type. Deklarieren und exportieren Sie zwei Funktionstypen: Predicate für einen Callback, der einen Wert und seinen Index nimmt und einen boolean zurückgibt, und Visitor für einen, der dasselbe nimmt und dessen Ergebnis ignoriert wird. Verwenden Sie sie statt Function.

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
  1. Hinweis 1

    Ein Funktionstyp-Ausdruck wird wie eine Arrow-Funktion geschrieben: (value: number, index: number) => boolean.

  2. Hinweis 2

    Der Callback des Visitors gibt die Zahl von push zurück. Welcher Rückgabetyp nimmt einen Callback an, egal was er zurückgibt?

  3. Hinweis 3

    export type Visitor = (value: number, index: number) => void; dann test: Predicate und visit: Visitor.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

export type Predicate = (value: number, index: number) => boolean;
export type Visitor = (value: number, index: number) => void;

export function countWhere(values: number[], test: Predicate): number {
  let count = 0;
  values.forEach((value, i) => {
    if (test(value, i)) count++;
  });
  return count;
}

export function each(values: number[], visit: Visitor): void {
  values.forEach((value, i) => visit(value, i));
}

export const passed = countWhere([72, 100, 64, 0], (score) => score >= 65);
export const labels: string[] = [];
each([72, 100], (score, i) => labels.push("#" + (i + 1) + "=" + score));
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 countWhere(values: number[], test: Function): number {
  let count = 0;
  values.forEach((value, i) => {
    if (test(value, i)) count++;
  });
  return count;
}

export function each(values: number[], visit: Function): void {
  values.forEach((value, i) => visit(value, i));
}

export const passed = countWhere([72, 100, 64, 0], (score) => score >= 65);
export const labels: string[] = [];
each([72, 100], (score, i) => labels.push("#" + (i + 1) + "=" + score));

main.test.ts

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {countWhere, each, passed, labels} from './main.ts';

test('countWhere zählt die Werte, die bestehen', () => {
  const n = countWhere([72, 100, 64, 0], (s) => s >= 65);
  assert.equal(n, 2, `countWhere([72, 100, 64, 0], s >= 65) gab ${n} zurück`);
  assert.equal(passed, 2, `passed ist ${passed}`);
});

test('countWhere übergibt auch den Index', () => {
  const n = countWhere([5, 0, 8], (v, i) => i > 0);
  assert.equal(n, 2, `countWhere mit i > 0 gab ${n} zurück`);
});

test('each ruft den Visitor mit jedem Wert und seinem Index auf', () => {
  const seen = [];
  each([7, 9], (v, i) => seen.push(v + '@' + i));
  assert.deepEqual(seen, ['7@0', '9@1'], `each besuchte ${JSON.stringify(seen)}`);
  assert.deepEqual(labels, ['#1=72', '#2=100'], `labels ist ${JSON.stringify(labels)}`);
});

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.ts

Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):

npx tsc
node --test
learnrun.js herunterladen

Übung 3 von 3

Projekt: ein Satz Overloads und ein never-Wächter

Stellen Sie die Bibliothek fertig. Schritt 1: Geben Sie fail den Rückgabetyp never, sodass required kompiliert. Schritt 2: Geben Sie last zwei Overloads über der Implementierung, last(text: string): string und last(values: number[]): number, sodass last("Ada").toUpperCase() kompiliert. Schritt 3: last darf für einen leeren String oder ein leeres Array nicht undefined zurückgeben: Rufen Sie zuerst fail mit der Meldung "last() needs at least one element" auf. Die Tests prüfen jeden Schritt.

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
  1. Hinweis 1

    Beginnen Sie mit den Fehlern von tsc: Einer steht in required, einer ist der Aufruf von toUpperCase. Jeder Schritt entfernt einen.

  2. Hinweis 2

    Overloads sind Köpfe, die mit ; enden und keinen Rumpf haben, direkt über der Implementierung, die ihre Union-Signatur behält.

  3. Hinweis 3

    In last vor dem return: if (input.length === 0) fail("last() needs at least one element");

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

export function fail(message: string): never {
  throw new Error(message);
}

export function required(value: number | undefined, name: string): number {
  if (value === undefined) fail(name + " is missing");
  return value;
}

export function last(text: string): string;
export function last(values: number[]): number;
export function last(input: string | number[]): string | number {
  if (input.length === 0) fail("last() needs at least one element");
  return input[input.length - 1];
}

export const initial: string = last("Ada").toUpperCase();
export const width = required(80, "width");
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 fail(message: string) {
  throw new Error(message);
}

export function required(value: number | undefined, name: string): number {
  if (value === undefined) fail(name + " is missing");
  return value;
}

export function last(input: string | number[]): string | number {
  return input[input.length - 1];
}

export const initial: string = last("Ada").toUpperCase();
export const width = required(80, "width");

main.test.ts

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {fail, required, last, initial, width} from './main.ts';

test('fail wirft einen Error mit der Meldung', () => {
  assert.throws(() => fail('stop'), {message: 'stop'}, 'fail("stop") soll einen Error mit der Meldung "stop" werfen');
});

test('required gibt einen vorhandenen Wert zurück und wirft bei einem fehlenden', () => {
  assert.equal(width, 80, `width ist ${width}`);
  assert.throws(() => required(undefined, 'height'), {message: 'height is missing'}, 'required(undefined, "height") soll "height is missing" werfen');
});

test('last gibt den letzten Buchstaben oder die letzte Zahl zurück', () => {
  assert.equal(last('Ada'), 'a', `last('Ada') gab ${JSON.stringify(last('Ada'))} zurück`);
  assert.equal(last([4, 2]), 2, `last([4, 2]) gab ${last([4, 2])} zurück`);
  assert.equal(initial, 'A', `initial ist ${JSON.stringify(initial)}`);
});

test('last wirft bei einem leeren String oder Array', () => {
  assert.throws(() => last([]), {message: 'last() needs at least one element'}, 'last([]) soll "last() needs at least one element" werfen');
  assert.throws(() => last(''), {message: 'last() needs at least one element'}, 'last("") soll "last() needs at least one element" werfen');
});

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.ts

Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):

npx tsc
node --test
learnrun.js herunterladen

Häufige Fehler

Ein Array an einen Rest-Parameter übergeben

function sum(...values: number[]): number {
  let total = 0;
  for (const value of values) total += value;
  return total;
}

const prices = [3, 4.5, 2];
console.log(sum(prices));

Was tsc oder Node.js ausgibt

main.ts(8,17): error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'number'.

Warum, und die Lösung

Ein Rest-Parameter sammelt einzelne Argumente, also muss jedes Argument eine Zahl sein, und prices ist ein Array. Verteilen Sie es: sum(...prices). Wenn Aufrufer meist ein Array haben, nehmen Sie stattdessen das Array: function sum(values: number[]).

Ein Prüf-Helfer ohne : never

function fail(message: string) {
  throw new Error(message);
}

function size(value: string | null): number {
  if (value === null) fail("value is null");
  return value.length;
}

Was tsc oder Node.js ausgibt

main.ts(7,10): error TS18047: 'value' is possibly 'null'.

Warum, und die Lösung

fail wirft immer, aber ohne Rückgabetyp inferiert tsc dafür void, also kann value nach dem if noch null sein. Deklarieren Sie function fail(message: string): never. Dann weiß tsc, dass der Code nach fail(...) nur erreicht wird, wenn value nicht null ist.

Ein Predicate, das einen Wert statt eines boolean zurückgibt

type Predicate = (value: number, index: number) => boolean;

function countWhere(values: number[], test: Predicate): number {
  let count = 0;
  values.forEach((value, i) => {
    if (test(value, i)) count++;
  });
  return count;
}

console.log(countWhere([72, 0], (s) => String(s)));

Was tsc oder Node.js ausgibt

main.ts(11,40): error TS2322: Type 'string' is not assignable to type 'boolean'.

Warum, und die Lösung

In JavaScript würde jedes truthy Ergebnis zählen, aber Predicate verspricht einen boolean. Geben Sie einen Vergleich zurück, etwa (s) => s !== 0 oder (s) => String(s) !== "". Ob die Bibliothek jedes Ergebnis annehmen soll, entscheidet ihr Typ, nicht ein einzelner Aufrufer.

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.

Problem melden

Etwas ist falsch oder unklar? Beschreiben Sie es kurz, dann wird es geprüft und korrigiert.

#

Mindestens 20 Zeichen.

Nur, wenn Sie eine Antwort wünschen.

Kernideen

Zuerst die Callback-Typen benennen

Eine Bibliothek wird von Code benutzt, den Sie nicht sehen; ihre Signaturen erklären sie. Benennen Sie jeden Callback-Typ mit einem Funktionstyp-Ausdruck: type Predicate = (value: number, index: number) => boolean. Dann prüft countWhere(values: number[], test: Predicate) jeden Callback eines Aufrufers. (s) => s >= 65 passt, denn ein Callback darf weniger Parameter nehmen; (s) => String(s) ist error TS2322: Type 'string' is not assignable to type 'boolean'. Ein Callback, dessen Ergebnis ignoriert wird, gibt void zurück, also passt (s, i) => lines.push("#" + i) zu Visitor = (value: number, index: number) => void. Der Typ Function prüft nichts: Die Callback-Parameter werden implizit any, error TS7006.

Flexible Parameter und ein Satz Overloads

Defaults decken den häufigen Fall ab: clamp(value: number, min = 0, max = 100) nimmt ein, zwei oder drei Argumente, und undefined wählt den Default. unit?: string ist optional, also prüfen Sie es vor der Verwendung auf undefined. Ein Rest-Parameter, ...values: number[], nimmt beliebig viele Zahlen; ein Array übergeben Sie als sum(...prices). Schreiben Sie Overloads nur, wo der Rückgabetyp vom Argument abhängt: last(text: string): string und last(values: number[]): number. Dann ist last("Ada") ein string, und last("Ada").toUpperCase() kompiliert; mit einer Signatur, die string | number zurückgibt, ist es error TS2339. Für beliebige Elementtypen braucht man Generics, die im Level Intermediate kommen.

Ein never-Helfer sichert die Ränder, Tests prüfen die Exporte

Typen sehen nicht jeden Wert: last("") ist laut Typ ein string, liefert aber undefined. Prüfen Sie zur Laufzeit mit function fail(message: string): never { throw new Error(message); }. Der Rückgabetyp zählt: Nach if (value === undefined) fail(...) weiß tsc, dass value eine Zahl ist. Ohne : never wird eine Funktionsdeklaration, die nur wirft, als void inferiert, und return value ist error TS2322. Testen Sie die Exporte mit node:test und importieren Sie sie aus main.ts. assert.equal(sum(1, 2), 3, "sum(1, 2)") prüft ein Ergebnis; assert.throws(() => last([]), { message: "last() needs at least one element" }) prüft, dass ein Aufruf wirft. Übergeben Sie assert.throws eine Funktion, nicht den Aufruf.

Quellen

Zuletzt geprüft am 4. Oktober 2026