number ist in TypeScript wie in JavaScript der einzige numerische Standardtyp — eine Trennung zwischen int und float gibt es nicht. Im Hintergrund steht IEEE 754 Double Precision, also 64-Bit-Gleitkomma. Daraus folgen drei Dinge, die du verinnerlichen solltest: Integer sind nur bis 2^53 − 1 exakt darstellbar, 0.1 + 0.2 ist nicht 0.3, und NaN ist selbst vom Typ number. TypeScript erlaubt darüber hinaus Number-Literal-Types (z. B. 1 | 2 | 3 | 4 | 5 | 6), mit denen du Wertemengen statisch eingrenzt. Dieser Artikel zeigt das Verhalten an konkreten Beispielen und liefert die Werkzeuge — Number.isFinite, Number.isInteger, Epsilon-Vergleich —, mit denen du robusten Code schreibst.
number als Primitive
Der Typ schreibt sich kleingeschrieben als number. Es gibt zusätzlich den Wrapper-Konstruktor Number aus JavaScript, der nicht als TypeScript-Typ verwendet werden sollte — er steht für Boxed-Number-Objekte und ist in echtem Code praktisch nie das, was du willst.
// Richtig: kleingeschrieben, primitive Form
let alter: number = 42;
let pi: number = 3.14159;
let negativ: number = -17.5;
// Falsch: Wrapper-Klasse — fast immer unbeabsichtigt
let kaputt: Number = 42; // technisch erlaubt, semantisch falschJavaScript — und damit TypeScript — kennt keine separaten Integer-Typen. Ob du 5 oder 5.0 oder 5.0000001 schreibst, alles ist number. Intern liegt jeder Wert als 64-Bit-IEEE-754-Double vor: 1 Bit Vorzeichen, 11 Bit Exponent, 52 Bit Mantisse. Das ist der Grund für alle Eigenheiten, die du in diesem Artikel siehst.
Die Sonderwerte Infinity, -Infinity und NaN (Not-a-Number) sind ebenfalls vom Typ number. Du kannst sie ganz normal in Variablen halten, vergleichen oder weiterreichen — der Compiler unterscheidet sie nicht von „normalen" Zahlen.
const unendlich: number = Infinity;
const ungueltig: number = NaN;
console.log(typeof unendlich); // "number"
console.log(typeof ungueltig); // "number"number
numberZahlen-Literale
TypeScript akzeptiert alle JavaScript-Schreibweisen für Zahlen-Literale. Die Form ist nur syntaktischer Zucker — intern landet jeder Wert als IEEE-754-Double.
const dezimal: number = 255;
const hex: number = 0xFF; // 255
const binaer: number = 0b1010; // 10
const oktal: number = 0o744; // 484
const exponential: number = 1e5; // 100_000
const float: number = 3.14e-2; // 0.0314
// Numerische Separatoren (ES2021) — nur fürs Auge
const million: number = 1_000_000;
console.log(dezimal, hex, binaer, oktal, exponential);255 255 10 484 100000Alle diese Literale sind gleichwertig — 0xFF === 255 ist true, weil beide denselben 64-Bit-Wert repräsentieren. Wähle die Schreibweise, die deine Absicht am klarsten zeigt: Bit-Masken als Hex oder Binär, Dateirechte als Oktal, große Werte mit Separatoren.
Number-Konstanten
Der Wrapper Number ist als Typ ungeeignet, als Namespace für Konstanten und statische Methoden aber unverzichtbar.
console.log(Number.MAX_VALUE); // groesste darstellbare Zahl
console.log(Number.MIN_VALUE); // kleinste positive Zahl > 0
console.log(Number.MAX_SAFE_INTEGER); // 2^53 - 1
console.log(Number.MIN_SAFE_INTEGER); // -(2^53 - 1)
console.log(Number.EPSILON); // kleinstes Inkrement um 1.0
console.log(Number.POSITIVE_INFINITY);
console.log(Number.NEGATIVE_INFINITY);1.7976931348623157e+308
5e-324
9007199254740991
-9007199254740991
2.220446049250313e-16
Infinity
-InfinityWas bedeuten die einzeln?
| Konstante | Bedeutung |
|---|---|
MAX_VALUE | Größte darstellbare positive Zahl, ca. 1.8 × 10^308. Alles darüber wird zu Infinity. |
MIN_VALUE | Nicht die kleinste negative Zahl — die ist -MAX_VALUE. Sondern die kleinste positive Zahl > 0 (5 × 10^-324). |
MAX_SAFE_INTEGER | Größte ganze Zahl, die ohne Präzisionsverlust dargestellt werden kann: 2^53 − 1 = 9_007_199_254_740_991. |
MIN_SAFE_INTEGER | Negatives Gegenstück. |
EPSILON | Abstand zwischen 1.0 und der nächstgrößeren darstellbaren Zahl. Praktisch als Toleranz für Gleitkomma-Vergleiche. |
Gleitkomma-Präzision
Das berühmteste Beispiel jeder JS-Einführung — und es trifft TypeScript genauso:
const summe = 0.1 + 0.2;
console.log(summe); // 0.30000000000000004
console.log(summe === 0.3); // false0.30000000000000004
falseDer Grund: 0.1 und 0.2 haben im Binärsystem keine endliche Darstellung — genauso wie 1/3 im Dezimalsystem ein periodischer Bruch ist. IEEE 754 rundet die Werte auf 52 Mantissenbits, und die Summe der gerundeten Werte ist eben minimal größer als 0.3.
Workaround: Epsilon-Vergleich. Statt auf Gleichheit zu testen, prüfst du, ob der Abstand kleiner als eine sinnvolle Toleranz ist. Die Konstante Number.EPSILON liefert genau diesen Maßstab für Werte um 1.
function fastGleich(a: number, b: number, toleranz = Number.EPSILON): boolean {
return Math.abs(a - b) < toleranz;
}
console.log(fastGleich(0.1 + 0.2, 0.3)); // true
console.log(fastGleich(1.0000001, 1.0000002)); // false (zu weit auseinander)true
falseFür sehr kleine oder sehr große Werte musst du die Toleranz relativ statt absolut wählen — Number.EPSILON ist nur in der Nähe von 1.0 der richtige Maßstab. Für Finanz- oder Buchhaltungs-Code bleibt der einzig wirklich sichere Weg: alles in der kleinsten Einheit (z. B. Cent als Integer) rechnen oder eine Decimal-Bibliothek nutzen.
NaN und das Typsystem
NaN (Not-a-Number) ist der Wert, der bei ungültigen numerischen Operationen entsteht: Math.sqrt(-1), 0/0, Number("foo"). Aus Sicht des Typsystems ist NaN vom Typ number — TypeScript hat keinen separaten NaN-Typ und keine Möglichkeit, ihn an der Signatur zu unterscheiden.
const x: number = NaN;
console.log(typeof x); // "number"
console.log(x === NaN); // false (!)
console.log(NaN === NaN); // false (!)number
false
falseNaN ist der einzige JS-Wert, der nicht gleich sich selbst ist. Der direkte Vergleich x === NaN funktioniert deshalb nie. Stattdessen gibt es zwei Hilfsfunktionen — die nicht dasselbe tun:
// Global: implizite Konversion vor dem Test — gefaehrlich
console.log(isNaN("hallo")); // true (String wird zu NaN konvertiert)
console.log(isNaN(undefined)); // true
// Number.isNaN: pruefte streng, keine Konversion
console.log(Number.isNaN("hallo")); // false
console.log(Number.isNaN(NaN)); // truetrue
true
false
trueFaustregel: Immer Number.isNaN benutzen. Die globale Variante ist ein Stolperdraht aus den frühen JS-Tagen.
Number-Literal-Types
TypeScript erlaubt konkrete Zahlen als Typen — nicht nur den allgemeinen number. In Unions ergeben sich daraus geschlossene Wertemengen, die der Compiler exhaustiv prüfen kann.
type Wuerfel = 1 | 2 | 3 | 4 | 5 | 6;
function wirf(): Wuerfel {
return (Math.floor(Math.random() * 6) + 1) as Wuerfel;
}
const ergebnis: Wuerfel = wirf();
// Compiler-Fehler: 7 ist nicht zuweisbar an Wuerfel
const falsch: Wuerfel = 7;Wofür ist das gut?
- API-Antworten modellieren: HTTP-Statuscodes als
200 | 201 | 204 | 400 | 404 | 500. - Diskriminierte Unions:
type Result = { code: 0; data: T } | { code: 1; error: string }. - Bit-Flags und Enum-Ersatz:
type LogLevel = 0 | 1 | 2 | 3ohne den Overhead echter Enums. - Sortier-Vergleiche:
function cmp(a, b): -1 | 0 | 1macht den Vertrag explizit.
Beachte: Bei let-Variablen weitet TypeScript Literal-Werte automatisch zu number aus (Literal Widening). Willst du den Literal-Typ erhalten, brauchst du const oder eine explizite Annotation bzw. as const.
const a = 42; // Typ: 42
let b = 42; // Typ: number
const c = 42 as const; // Typ: 42
const punkt = { x: 1, y: 2 }; // x, y: number
const punktFest = { x: 1, y: 2 } as const; // x: 1, y: 2 (readonly)Konvertierung
Strings und andere Werte in number umwandeln — JavaScript bietet vier Wege mit unterschiedlichen Semantiken:
console.log(Number("42")); // 42
console.log(Number("42.5")); // 42.5
console.log(Number("")); // 0 (!)
console.log(Number(" ")); // 0 (!)
console.log(Number(null)); // 0 (!)
console.log(Number(undefined)); // NaN
console.log(Number(true)); // 1
console.log(Number("42abc")); // NaN
console.log(Number("0x10")); // 16
console.log(parseInt("42abc")); // 42 (parsed bis zum Fehler)
console.log(parseInt("0x10")); // 16
console.log(parseInt("42.9")); // 42 (Dezimalstellen abgeschnitten)
console.log(parseFloat("42.9abc"));// 42.9
console.log(parseFloat("0x10")); // 0 (kein Hex bei parseFloat)
console.log(+"42"); // 42 (unaeres Plus, wie Number())
console.log(+""); // 0 (!)42
42.5
0
0
0
NaN
1
NaN
16
42
16
42
42.9
0
42
0Wichtige Unterschiede:
| Funktion | Verhalten bei "" | Bei "42abc" | Hex/Bin? |
|---|---|---|---|
Number(x) / +x | 0 | NaN | Ja (0x, 0b, 0o) |
parseInt(x, 10) | NaN | 42 | Nur mit 0x, immer Radix mitgeben |
parseFloat(x) | NaN | 42 | Nein |
parseInt ohne zweiten Parameter ist ein historischer Stolperstein: alte Engines interpretierten "08" als Oktal und gaben 0 zurück. Moderne Engines behandeln das als Dezimal — aber immer explizit parseInt(input, 10) schreiben, dann gibt es keine Diskussion.
Type Guards für number
In Code, der von außen kommt — JSON-APIs, Formulareingaben, localStorage —, weißt du nie, was tatsächlich ankommt. Drei Guards, die du kennen solltest:
function istZahl(wert: unknown): wert is number {
return typeof wert === "number";
}
// Problem: typeof NaN === "number" — auch NaN ist eine "Zahl"
function istEchteZahl(wert: unknown): wert is number {
return typeof wert === "number" && Number.isFinite(wert);
}
function istGanzeZahl(wert: unknown): wert is number {
return typeof wert === "number" && Number.isInteger(wert);
}Number.isFinite ist die strenge, ehrliche Variante der globalen isFinite-Funktion:
// Global: konvertiert vorher implizit
console.log(isFinite("42")); // true (!)
console.log(isFinite("")); // true (!) "" -> 0
console.log(isFinite(null)); // true (!) null -> 0
// Number.isFinite: keine Konversion, exakt
console.log(Number.isFinite("42")); // false
console.log(Number.isFinite(42)); // true
console.log(Number.isFinite(NaN)); // false
console.log(Number.isFinite(Infinity)); // falsetrue
true
true
false
true
false
falsePraxis-Pattern für Eingabe-Validierung:
function parseEingabe(roh: string): number | null {
const trimmed = roh.trim();
if (trimmed === "") return null; // explizit ablehnen
const n = Number(trimmed);
return Number.isFinite(n) ? n : null;
}
console.log(parseEingabe("42")); // 42
console.log(parseEingabe(" 3.14 ")); // 3.14
console.log(parseEingabe("")); // null
console.log(parseEingabe("foo")); // null
console.log(parseEingabe("Infinity"));// nullHäufige Stolperfallen
NaN === NaN ist immer false.
NaN ist der einzige JavaScript-Wert, der nicht gleich sich selbst ist. Direkter Vergleich liefert nie ein sinnvolles Ergebnis. Immer Number.isNaN(x) verwenden — niemals x === NaN.
Gleitkomma-Vergleiche brauchen einen Toleranzbereich.
0.1 + 0.2 === 0.3 ist false, weil IEEE 754 dezimale Brüche nicht exakt darstellen kann. Statt === mit Math.abs(a - b) < Number.EPSILON arbeiten — und für Beträge in Geld grundsätzlich in der kleinsten Einheit (Cent) als Integer rechnen.
parseInt ohne Radix-Parameter.
Alte JS-Engines behandelten Strings wie 08 oder 011 als Oktal, was zu Null-Ergebnissen führte. Moderne Engines sind hier fix, aber gewöhne dir an, immer parseInt(wert, 10) zu schreiben — explizit ist sicherer als auf Engine-Defaults zu vertrauen.
Number("") ergibt 0, nicht NaN.
Leere Strings, reine Whitespace-Strings und null werden alle still zu 0. Für Formular-Validierung katastrophal: ein leeres Feld wird als „Null Euro" akzeptiert. Vorher mit trim() auf leeren String prüfen und gezielt ablehnen.
MAX_SAFE_INTEGER ist eine harte Grenze.
Ab 2^53 verlierst du Präzision: 9007199254740992 === 9007199254740993 ist true, weil beide Werte denselben IEEE-754-Repräsentanten haben. Für IDs aus 64-Bit-Datenbanken, Twitter-Snowflake-IDs oder Krypto-Beträge unbedingt bigint oder String-Repräsentation nutzen.
number deckt nicht alle ganzen Zahlen ab.
Die intuitive Annahme „eine Zahl ist eine Zahl" stimmt nur bis 53 Bit. Darüber kannst du keine Integer-Arithmetik mehr verlässlich ausführen — Inkremente werden gerundet, Vergleiche werden falsch. Number.isSafeInteger ist der Test, ob ein Wert noch im sicheren Bereich liegt.
Globaler isFinite macht implizite Konversion.
isFinite("") ist true, weil der leere String vorher zu 0 konvertiert wird. Number.isFinite("") ist false — keine Konversion, ehrliche Antwort. Dasselbe gilt für isNaN vs. Number.isNaN. Faustregel: immer die Number.*-Variante.
Number (Wrapper-Klasse) niemals als Typ benutzen.
Der Großbuchstaben-Typ Number bezeichnet das Box-Objekt, nicht den Primitive. const x: Number = 42 kompiliert, ist aber falsch — du kannst damit keine arithmetischen Operationen sauber ausführen und du verlierst Type-Narrowing. Immer kleingeschrieben: const x: number = 42.