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.

ts
// 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 falsch

JavaScript — 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.

ts
const unendlich: number = Infinity;
const ungueltig: number = NaN;

console.log(typeof unendlich); // "number"
console.log(typeof ungueltig); // "number"
Output
number
number

Zahlen-Literale

TypeScript akzeptiert alle JavaScript-Schreibweisen für Zahlen-Literale. Die Form ist nur syntaktischer Zucker — intern landet jeder Wert als IEEE-754-Double.

ts
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);
Output
255 255 10 484 100000

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

ts
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);
Output
1.7976931348623157e+308
5e-324
9007199254740991
-9007199254740991
2.220446049250313e-16
Infinity
-Infinity

Was bedeuten die einzeln?

KonstanteBedeutung
MAX_VALUEGrößte darstellbare positive Zahl, ca. 1.8 × 10^308. Alles darüber wird zu Infinity.
MIN_VALUENicht die kleinste negative Zahl — die ist -MAX_VALUE. Sondern die kleinste positive Zahl > 0 (5 × 10^-324).
MAX_SAFE_INTEGERGrößte ganze Zahl, die ohne Präzisionsverlust dargestellt werden kann: 2^53 − 1 = 9_007_199_254_740_991.
MIN_SAFE_INTEGERNegatives Gegenstück.
EPSILONAbstand 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:

ts
const summe = 0.1 + 0.2;

console.log(summe);              // 0.30000000000000004
console.log(summe === 0.3);      // false
Output
0.30000000000000004
false

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

ts
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)
Output
true
false

Fü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.

ts
const x: number = NaN;

console.log(typeof x);   // "number"
console.log(x === NaN);  // false (!)
console.log(NaN === NaN); // false (!)
Output
number
false
false

NaN 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:

ts
// 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));     // true
Output
true
true
false
true

Faustregel: 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.

ts
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 | 3 ohne den Overhead echter Enums.
  • Sortier-Vergleiche: function cmp(a, b): -1 | 0 | 1 macht 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.

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

ts
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  (!)
Output
42
42.5
0
0
0
NaN
1
NaN
16
42
16
42
42.9
0
42
0

Wichtige Unterschiede:

FunktionVerhalten bei ""Bei "42abc"Hex/Bin?
Number(x) / +x0NaNJa (0x, 0b, 0o)
parseInt(x, 10)NaN42Nur mit 0x, immer Radix mitgeben
parseFloat(x)NaN42Nein

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:

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

ts
// 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)); // false
Output
true
true
true
false
true
false
false

Praxis-Pattern für Eingabe-Validierung:

ts
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"));// null

Hä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.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Primitive Typen

Zur Übersicht