Zum Inhalt springen
aviral gupta

// B2.2 · ca. 30 Min. · Einstieg

Annotationen und Typableitung

Nach dieser Lektion annotieren Sie Variablen, Parameter und Rückgabetypen, sagen vorher, was TypeScript ohne sie ableitet, und lassen Annotationen weg, die es nur wiederholen.

Lektion 2 von 5 in B2 Alltägliche Typen

Danach können Sie

  • Variablen, Parameter und Rückgabetypen annotieren und sagen, wo jede Annotation steht
  • Den Typ vorhersagen, den TypeScript aus einem Wert, einem gemischten Array und den return-Anweisungen einer Funktion ableitet
  • Kontextuelle Typisierung bei Callbacks erkennen und entscheiden, wann eine Annotation nötig ist
  1. Aufwärmen · Aufgabe 1 von 7

    Aufwärmen mit den letzten Lektionen: Welche dieser Zeilen enthalten eine Typannotation? Wählen Sie alle zutreffenden.

    Wählen Sie alle zutreffenden aus.

  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie es vorher, bevor Sie weiterlesen: Welchen Typ leitet TypeScript für mixed ab?

    const mixed = [1, "two", 3];
  3. Üben · Aufgabe 3 von 7

    Ergänzen Sie den Rückgabetyp, damit tsc die Funktion akzeptiert und sie 3.5 ausgibt.

    function average(a: number, b: number): ____ {
      return (a + b) / 2;
    }
    
    console.log(average(3, 4));
    function average(a: number, b: number): {
  4. Üben · Aufgabe 4 von 7

    Der Callback hat keine Annotation. Welchen Typ leitet TypeScript für lengths ab?

    const names = ["Ada", "Grace"];
    const lengths = names.map((name) => name.length);
  5. Üben · Aufgabe 5 von 7

    Der Rückgabetyp sagt string. Was meldet tsc?

    function grade(points: number): string {
      if (points >= 50) {
        return "pass";
      }
    }
  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. Beide Zeilen enthalten dieselbe Pfeilfunktion (n) => n * 2, und keines der n hat eine Annotation. Welche Zeilen meldet tsc?

    const double = (n) => n * 2;
    console.log([1, 2, 3].map((n) => n * 2));
  7. Anwenden · Aufgabe 7 von 7

    Mini-Aufgabe. Beginnen Sie mit const words = ["tree", "sky", "river"]. Behalten Sie mit filter die Wörter mit mehr als 3 Buchstaben, machen Sie sie mit map zu Großbuchstaben und verbinden Sie sie mit einer Funktion joinAll(items: string[]): string. Annotieren Sie nur, was es braucht: den Parameter und den Rückgabetyp von joinAll. Callbacks und Variablen bekommen keine Annotation. Führen Sie npx tsc und node main.ts aus; es gibt TREE, RIVER aus. Schreiben Sie dann toUpperCase im map-Callback falsch und lesen Sie die Meldung: Die kontextuelle Typisierung wusste, dass word ein String ist.

    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 Bestellsumme, halb annotiert

Die Kommentare sagen, welche Typen geschrieben sind und welche TypeScript ableitet. Die Parameter der beiden deklarierten Funktionen sind annotiert; prices, label, der Rückgabetyp von sum und price im Callback werden abgeleitet. Führen Sie npx tsc aus, das nichts ausgibt, dann node main.ts. Probieren Sie danach label = 42 oder prices.push("free"): tsc meldet beides, obwohl keine der Variablen eine Annotation hat.

main.ts

// Which types are written, and which does TypeScript infer?
const prices = [4.5, 12, 3.25]; // inferred: number[]
const discount: number = 0.1; // annotated, though 0.1 would give number anyway
let label = "Order"; // inferred: string

function applyDiscount(amount: number, rate: number): number {
  return amount * (1 - rate);
}

// No return type written: TypeScript infers number from the return statement.
function sum(values: number[]) {
  let total = 0;
  for (const value of values) {
    total += value;
  }
  return total;
}

// price needs no annotation: map passes the elements of a number[].
const discounted = prices.map((price) => applyDiscount(price, discount));
label = label + " #42";
console.log(label);
console.log("before:", sum(prices));
console.log("after:", sum(discounted).toFixed(2));

Ausführen mit

npx tsc
node main.ts

Ausgabe

Order #42
before: 19.75
after: 17.78
  • prices ist number[] und label ein string, beides aus dem ersten Wert abgeleitet.
  • sum hat keinen Rückgabetyp, und doch besteht sum(discounted).toFixed(2) die Prüfung: TypeScript hat number abgeleitet.
  • price im map-Callback ist aus seinem Kontext eine Zahl, also wird applyDiscount(price, discount) geprüft.
  • Die Annotation bei discount ändert nichts; sie wiederholt nur, was 0.1 ergäbe.
Ä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

Nur annotieren, was es braucht

Beide Funktionen funktionieren, aber tsc meldet viermal Parameter … implicitly has an 'any' type. Annotieren Sie die drei Parameter der deklarierten Funktionen: values ist eine Liste von Zahlen, words eine Liste von Texten, minLength eine Zahl. Annotieren Sie word im Callback nicht: Sobald words seinen Typ hat, verschwindet der vierte Fehler von selbst. Rückgabetypen sind hier freiwillig.

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

    Eine Liste von Zahlen ist number[], eine Liste von Texten string[].

  2. Hinweis 2

    Der Callback (word) => … bekommt seinen Typ von words.filter: Sobald words ein string[] ist, ist word ein String.

  3. Hinweis 3

    Die ersten Zeilen werden zu average(values: number[]) und longWords(words: string[], minLength: number).

Eine Lösung zeigen

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

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

export function longWords(words: string[], minLength: number): string[] {
  return words.filter((word) => word.length >= minLength);
}
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 average(values) {
  let sum = 0;
  for (const value of values) {
    sum += value;
  }
  return sum / values.length;
}

export function longWords(words, minLength) {
  return words.filter((word) => word.length >= minLength);
}

main.test.ts

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {average, longWords} from './main.ts';

test('average([2, 4, 9]) ist 5', () => {
  assert.equal(average([2, 4, 9]), 5, 'average([2, 4, 9]) sollte 5 sein');
});

test('longWords behält die Wörter mit mindestens 3 Buchstaben', () => {
  assert.deepEqual(longWords(['a', 'tree', 'sky'], 3), ['tree', 'sky'], 'longWords(["a", "tree", "sky"], 3) sollte ["tree", "sky"] 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.ts

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

npx tsc
node --test
learnrun.js herunterladen

Übung 2 von 2

Den Fehler vom Rückgabetyp finden lassen

formatScore soll immer Text liefern: ab 50 Prozent "50% (pass)", darunter zum Beispiel "25% (fail)". Ihr Rückgabetyp ist als string annotiert, und tsc meldet die Zeile, die dieses Versprechen bricht. Korrigieren Sie diese Zeile so, dass sie den Prozentwert gefolgt von % (fail) zurückgibt. Lassen Sie die Annotationen, wie sie sind.

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

    tsc meldet main.ts(6,3): error TS2322: Type 'number' is not assignable to type 'string'. Zeile 6 gibt die nackte Zahl zurück.

  2. Hinweis 2

    Bauen Sie den Text wie in Zeile 4, mit toFixed(0) und einer Endung.

  3. Hinweis 3

    Zeile 6 wird zu return percent.toFixed(0) + "% (fail)";

Eine Lösung zeigen

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

export function formatScore(points: number, max: number): string {
  const percent = (points / max) * 100;
  if (percent >= 50) {
    return percent.toFixed(0) + "% (pass)";
  }
  return percent.toFixed(0) + "% (fail)";
}
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 formatScore(points: number, max: number): string {
  const percent = (points / max) * 100;
  if (percent >= 50) {
    return percent.toFixed(0) + "% (pass)";
  }
  return percent;
}

main.test.ts

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {formatScore} from './main.ts';

test('20 von 40 ist bestanden', () => {
  assert.equal(formatScore(20, 40), '50% (pass)', 'formatScore(20, 40) sollte "50% (pass)" sein');
});

test('10 von 40 ist nicht bestanden', () => {
  assert.equal(formatScore(10, 40), '25% (fail)', `formatScore(10, 40) sollte "25% (fail)" sein, war aber ${JSON.stringify(formatScore(10, 40))}`);
});

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

Eine Pfeilfunktion ohne Kontext

const double = (n) => n * 2;
console.log(double(21));

Was tsc oder Node.js ausgibt

main.ts(1,17): error TS7006: Parameter 'n' implicitly has an 'any' type.

Warum, und die Lösung

Kontextuelle Typisierung wirkt nur dort, wo die Funktion an eine Stelle übergeben wird, die ihre Parameter festlegt, etwa map oder forEach. Eine Funktion in einer Variablen hat keinen solchen Kontext, also kann TypeScript n nicht ableiten, und strict meldet es. Annotieren Sie den Parameter: const double = (n: number) => n * 2. Der Rückgabetyp wird dann als number abgeleitet.

Mit einem Element eines gemischten Arrays rechnen

const scores = [90, "85", 70];
const doubled = scores[1] * 2;
console.log(doubled);

Was tsc oder Node.js ausgibt

main.ts(2,17): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.

Warum, und die Lösung

Der beste gemeinsame Typ von 90, "85" und 70 ist (string | number)[], also könnte jedes Element Text sein, und * braucht links eine Zahl. JavaScript gäbe stillschweigend 170 aus. Korrigieren Sie die Daten, nicht die Prüfung: Schreiben Sie 85 ohne Anführungszeichen, dann wird scores zu number[]. Kommt der Text wirklich von außen, wandeln Sie ihn einmal mit Number("85") um.

Ein Rumpf, der seinen Rückgabetyp bricht

function isAdult(age: number): boolean {
  return age >= 18 ? "yes" : "no";
}

console.log(isAdult(20));

Was tsc oder Node.js ausgibt

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

Warum, und die Lösung

Die Annotation verspricht einen boolean, und tsc prüft jeden zurückgegebenen Wert dagegen: "yes" (Spalte 22) und "no" (Spalte 30) werden beide gemeldet. Ohne die Annotation würde TypeScript einen String als Rückgabetyp ableiten, und Aufrufer bekämen "yes" statt true. Geben Sie den Vergleich selbst zurück: return age >= 18;

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

Wo Annotationen stehen und welche Sie brauchen

Eine Annotation steht immer hinter dem, was sie typisiert: let total: number = 0, ein Parameter als (price: number) und ein Rückgabetyp hinter der Parameterliste: function isAdult(age: number): boolean. Parameter einer Funktion, die Sie deklarieren, brauchen eine; ohne sie meldet strict error TS7006. Ein Rückgabetyp ist freiwillig, weil TypeScript ihn aus den return-Anweisungen ableitet. Steht er da, prüft tsc den Rumpf dagegen: return "yes" in einer boolean-Funktion wird beim return gemeldet, und ein Pfad ohne return ergibt error TS2366. Eine Variable mit Wert braucht selten eine: TypeScript leitet den Typ aus dem Wert ab, und eine Annotation, die dasselbe sagt, wiederholt ihn nur.

Was TypeScript ableitet

Eine Variable ohne Annotation bekommt den Typ ihres ersten Werts: let count = 3 ist eine Zahl, und später Text zuzuweisen ist ein Fehler. Eine Funktion ohne Rückgabetyp bekommt den Typ dessen, was sie zurückgibt: function half(n: number) { return n / 2; } liefert number. Bei einem Array prüft TypeScript jedes Element und sucht einen besten gemeinsamen Typ: [1, 2, 3] ist number[], aber [1, "two", 3] ist (string | number)[] (jedes Element String oder Zahl), und [0, 1, null] ist (number | null)[]. Mit scores = [90, "85", 70] ist scores[1] * 2 daher ein Fehler. Eine const bekommt einen noch engeren Typ: const limit = 3 hat den Typ 3, einen Literaltyp (Lektion B2.5).

Kontextuelle Typisierung: Callbacks brauchen keine Annotationen

Typableitung wirkt auch von außen nach innen. In names.map((name) => name.length), wobei names ein string[] ist, weiß TypeScript, dass map jedes Element an den Callback übergibt: name ist ein String, das Ergebnis number[]. Das ist kontextuelle Typisierung: Wo eine Funktion steht, bestimmt ihre Parametertypen. Auch der Rumpf des Callbacks wird geprüft: name.toUppercase() wird mit Did you mean 'toUpperCase'? gemeldet. Dieselbe Pfeilfunktion allein gespeichert, const double = (n) => n * 2, hat keinen Kontext, also ist n error TS7006. Als Faustregel: Annotieren Sie die Parameter deklarierter Funktionen und Rückgabetypen, wo der Rumpf geprüft werden soll; Callbacks und Variablen mit Wert überlassen Sie der Typableitung.

Quellen

Zuletzt geprüft am 30. September 2026