JavaScript mit Typen: weniger Fehler, bessere Werkzeuge
Ausführen: Mit Node.js: npm install -D typescript, dann npx tsc hallo.ts (erzeugt hallo.js) und node hallo.js. Neuere Node-Versionen führen einfache .ts-Dateien direkt aus (node hallo.ts). Zum Ausprobieren eignet sich der TypeScript-Playground auf typescriptlang.org.
tsc --noEmit prüft Typen, tsc übersetzt zusätzlichfunction flaeche(breite, hoehe) {
return breite * hoehe;
}
console.log(flaeche(3, 4)); // 12
console.log(flaeche("3", "x")); // NaN: kein Fehler, nur falsches Ergebnis
console.log(flaeche(3)); // NaN: Parameter vergessenstring, number, boolean, null, undefined, Arrays als typ[]unknown statt any: erst prüfen, dann nutzenstrict sind null/undefined eigene Typenas behauptet einen Typ ohne Prüfungconst name: string = "Mia";
const alter: number = 17;
const aktiv: boolean = true;
const werte: number[] = [1, 2, 3];
const noten: Array<number> = [1, 2]; // gleichbedeutend
console.log(name, alter, aktiv, werte.length, noten.length);x?: T, Standardwerte x = 1, Rest ...x: T[](a: number) => stringvoid = nichts zurück, never = kehrt nie zurückasync liefert Promise<T>function addiere(a: number, b: number): number {
return a + b;
}
const mal = (a: number, b: number): number => a * b;
console.log(addiere(2, 3), mal(4, 5));name: string, optional x?: T, schreibgeschützt readonlyinterface und type sind für Objekte fast austauschbar; type kann zusätzlich Unions und Tupelextends (interface) oder & (type)Record<string, T> und Index-Signaturen für beliebige Schlüsseltype Nutzer = {
id: number;
name: string;
email?: string; // optional
readonly angelegt: string; // nur lesbar
};
const mia: Nutzer = { id: 1, name: "Mia", angelegt: "2025-01-01" };
console.log(mia.name, mia.email ?? "keine E-Mail");A | B: Wert ist einer der Typen; innerhalb von Prüfungen wird der Typ eingeengt (Narrowing)"links" | "rechts") ersetzen magische Stringsart) bestimmt die Variantenever im default erzwingt Vollständigkeitx is Typfunction formatiere(wert: string | number): string {
if (typeof wert === "string") {
return wert.toUpperCase(); // hier ist wert: string
}
return wert.toFixed(2); // hier ist wert: number
}
console.log(formatiere("hallo"), formatiere(3.14159));T[] oder Array<T>; readonly T[] ist unveränderlichenum oft besser: as const-Objekte oder Union-Literaltypentypeof x[number] und keyof typeof leiten Typen aus Werten abMap<K, V> und Set<T> sind generisch typisiertconst zahlen: number[] = [3, 1, 2];
const namen: Array<string> = ["Mia", "Tom"];
const gemischt: (string | number)[] = [1, "zwei", 3];
const matrix: number[][] = [[1, 2], [3, 4]];
zahlen.sort((a, b) => a - b);
console.log(zahlen, namen.map((n) => n.toUpperCase()), gemischt.length, matrix[1][0]);public, private, protected, readonly; #name ist echt privatimplements erzwingt, dass eine Klasse ein Interface erfülltabstract für Vorlagen; static für Klassenebeneclass Konto {
readonly inhaber: string;
#stand: number = 0; // echt privat (JavaScript)
private log: string[] = []; // nur zur Compile-Zeit privat (TypeScript)
constructor(inhaber: string, startwert = 0) {
this.inhaber = inhaber;
this.#stand = startwert;
}
einzahlen(betrag: number): this {
if (betrag <= 0) throw new RangeError("Betrag muss positiv sein");
this.#stand += betrag;
this.log.push(`+${betrag}`);
return this;
}
get stand(): number {
return this.#stand;
}
verlauf(): string[] {
return [...this.log];
}
}
const k = new Konto("Mia", 100).einzahlen(50).einzahlen(25);
console.log(k.inhaber, k.stand, k.verlauf());function f<T>(x: T): TT extends ...; Schlüssel mit K extends keyof Tfunction erstesAny(liste: any[]): any {
return liste[0];
}
const x = erstesAny([1, 2, 3]); // x: any, alle Information ist verlorenPartial, Required, Readonly, Pick, Omit, Record formen Objekttypen umReturnType, Parameters, Awaited leiten Typen aus Funktionen abtypeof und keyof leiten Typen aus Werten und Schlüsseln ab[K in keyof T]) und bedingte Typen (T extends U ? X : Y) bauen eigene Hilfstypensatisfies prüft gegen einen Typ, ohne den genauen Typ zu verliereninterface Nutzer {
id: number;
name: string;
email: string;
alter?: number;
}import/export; import type für reine Typentsconfig.json steuert den Compiler; strict: true immer einschaltentsc --noEmit prüft nur; Bundler (Vite, esbuild) übersetzen schnellallowJs, // @ts-check, dann Dateien umbenennenisolatedModules und skipLibCheck sind gängige Empfehlungen// typen.ts
export interface Nutzer { id: number; name: string }
export type Rolle = "admin" | "nutzer";
export const standardRolle: Rolle = "nutzer";
// dienst.ts
import { type Nutzer, standardRolle } from "./typen.js";
import type { Rolle } from "./typen.js"; // reiner Typ-Import: wird komplett entfernt
export function neuerNutzer(name: string): Nutzer & { rolle: Rolle } {
return { id: Date.now(), name, rolle: standardRolle };
}async-Funktionen liefern Promise<T>; Promise.all behält Tupel-Typencatch (e): e ist unknown, erst mit instanceof prüfenJSON.parse und response.json() liefern any: über unknown und Type Guards oder Zod prüfenas zu behaupten ersetzt keine Prüfunginterface Nutzer { id: number; name: string }
function hole(id: number): Promise<Nutzer> {
return new Promise((resolve, reject) => {
setTimeout(() => (id > 0 ? resolve({ id, name: "Nutzer " + id }) : reject(new Error("ungültig"))), 10);
});
}
async function main(): Promise<void> {
const n = await hole(1); // n: Nutzer
console.log(n.name);
try {
await hole(-1);
} catch (e) {
console.log(e instanceof Error ? e.message : "unbekannt");
}
const alle = await Promise.all([hole(1), hole(2)]); // [Nutzer, Nutzer]
console.log(alle.map((x) => x.id));
}
main();tsconfig.json die Bibliothek DOMquerySelector kann null liefern: prüfen oder mit Hilfsfunktion absichernMouseEvent, KeyboardEvent, SubmitEvent@types/node installieren; node:-Importe nutzen.d.ts-Dateien ergänzen fehlende globale Typenconst knopf = document.querySelector("button"); // HTMLButtonElement | null
const feld = document.querySelector<HTMLInputElement>("#name"); // Typ angeben
knopf?.addEventListener("click", (ereignis) => { // ereignis: MouseEvent
console.log(ereignis.clientX, feld?.value);
});
const liste = document.getElementById("liste"); // HTMLElement | null
if (liste) {
const li = document.createElement("li");
li.textContent = "Neuer Eintrag";
liste.append(li);
}@types/<paket>.d.ts-Dateien beschreiben Typen ohne Code (declare)import type für reine Typ-Importe; Module lassen sich erweiternas, !, @ts-ignore und any sind Notausgänge, @ts-expect-error ist besser als ignore// mathe-lib.d.ts
declare module "mathe-lib" {
export function addiere(a: number, b: number): number;
export const PI: number;
export interface Optionen { runden?: boolean }
export default function rechne(ausdruck: string, optionen?: Optionen): number;
}strict, unknown statt any, Daten von außen validierentsc --noEmit, typescript-eslint, Prettier, Testsinterface AnfrageSchlecht {
laedt: boolean;
daten?: string[];
fehler?: string; // laedt true UND fehler gesetzt? Was gilt?
}type Alias = string | number;
interface Form { name: string; flaeche(): number }
interface Kreis extends Form { radius: number }
class Hund implements Form { /* ... */ name = ""; flaeche() { return 0; } }
abstract class Basis { abstract m(): void }
enum Farbe { Rot, Gruen }
const obj = { a: 1 } as const;
function f<T extends object>(x: T): T { return x; }
declare const global: string;
export type { Alias };
export default f;