Der Primitive-Typ bigint ist TypeScripts Antwort auf eine alte JavaScript-Schwäche: number ist ein IEEE-754-Double und kann Ganzzahlen nur bis 2^53 - 1 exakt darstellen. Darüber kippt die Präzision lautlos um — 9007199254740993 wird zu 9007199254740992. bigint löst das mit beliebig großer Präzision und einer eigenen Literal-Syntax (100n). Der Preis: ein Pflicht-target: es2020 in der tsconfig.json, ein striktes Mischverbot mit number und ein paar überraschende Eigenheiten wie nicht-JSON-serialisierbare Werte. Dieser Artikel zeigt, wann du bigint brauchst, wie der Compiler ihn behandelt und welche Fallen warten.

Wozu bigint?

JavaScripts number ist ein 64-bit-Double nach IEEE 754. Das reicht für die meisten Berechnungen — bis du eine Integer brauchst, die größer ist als Number.MAX_SAFE_INTEGER (2^53 - 1, also 9007199254740991). Ab diesem Punkt verliert number lautlos Genauigkeit:

ts precision-loss.ts
const max = Number.MAX_SAFE_INTEGER; // 9007199254740991
console.log(max + 1); // 9007199254740992
console.log(max + 2); // 9007199254740992 — gleiche Zahl!
console.log(max + 3); // 9007199254740994 — Sprung um 2

console.log(9007199254740993 === 9007199254740992); // true
Output
9007199254740992
9007199254740992
9007199254740994
true

bigint ist eine arbitrary-precision integer: beliebig viele Stellen, ohne Genauigkeitsverlust. Eingeführt wurde der Typ mit TypeScript 3.2 und ist heute in allen modernen Engines verfügbar.

ts bigint-precision.ts
const big: bigint = 9007199254740993n;
console.log(big);              // 9007199254740993n
console.log(big + 1n);         // 9007199254740994n
console.log(big * big);        // 81129638414606735407491456n
Output
9007199254740993n
9007199254740994n
81129638414606735407491456n

bigint-Literale

bigint-Literale sind normale Ganzzahl-Literale mit angehängtem n. Die üblichen Zahlensysteme funktionieren alle:

ts literals.ts
const dezimal: bigint  = 100n;
const hex: bigint      = 0xFFn;     // 255n
const oktal: bigint    = 0o744n;    // 484n
const binaer: bigint   = 0b1010n;   // 10n
const negativ: bigint  = -42n;

// Compile-Fehler: bigint kann keine Nachkommastellen darstellen
const falsch: bigint = 3.14n;
//                     ~~~~~ A bigint literal must be an integer.

Ein Float-bigint existiert nicht — der ganze Sinn des Typs ist, dass es Ganzzahlen sind. Wer Kommazahlen mit hoher Präzision braucht, greift zu einer Library wie decimal.js.

target-Anforderung in der tsconfig.json

bigint ist Sprachfeature von ES2020. Wer ein älteres target setzt, bekommt vom Compiler einen klaren Hinweis:

ts error-old-target.ts
// tsconfig.json: "target": "es2019"
const big = 100n;
//          ~~~~ BigInt literals are not available when targeting
//               lower than ES2020.

Korrekte Mindest-Konfiguration:

jsonc tsconfig.json
{
  "compilerOptions": {
    "target": "es2020",
    "lib": ["es2020", "dom"],
    "strict": true
  }
}

Grund: bigint hat eigene Operator-Semantik (+, *, / etc.), die sich nicht trivial in älteres JavaScript downlevern lässt. Die Engine muss die Arithmetik nativ kennen.

Konvertierung zwischen bigint und number

Mit der globalen BigInt(...)-Funktion (ohne new) erzeugst du bigint-Werte aus number, String oder Boolean:

ts conversion.ts
const ausZahl: bigint   = BigInt(100);        // 100n
const ausString: bigint = BigInt("9999999999999999999"); // beliebig groß
const ausHex: bigint    = BigInt("0xff");     // 255n

// Rückweg: explizit Number(...)
const klein: bigint = 100n;
const num: number   = Number(klein);          // 100

// Präzisionsverlust beim Rückweg, wenn der Wert zu groß ist
const riesig: bigint = 9007199254740993n;
console.log(Number(riesig)); // 9007199254740992 — Stelle verloren
Output
9007199254740992

Wichtig: BigInt(0.5) ist ein Laufzeitfehler (RangeError). Floats lassen sich nicht in bigint konvertieren, weil das per Definition Information vernichten würde.

Arithmetik mit bigint

Alle üblichen Operatoren funktionieren — und das Ergebnis ist immer ein bigint:

ts arithmetic.ts
console.log(10n + 3n);  // 13n
console.log(10n - 3n);  // 7n
console.log(10n * 3n);  // 30n
console.log(10n / 3n);  // 3n  — Integer-Division, abgeschnitten!
console.log(10n % 3n);  // 1n
console.log(2n ** 64n); // 18446744073709551616n
Output
13n
7n
30n
3n
1n
18446744073709551616n

Die Division ist der größte Stolperstein: 5n / 2n ergibt 2n, nicht 2.5n. bigint kennt keine Nachkommastellen, also schneidet / Richtung Null ab. Wer Rest braucht, kombiniert mit %.

Mischverbot mit number

Hier ist TypeScript besonders streng — und JavaScript zur Laufzeit ebenfalls. Du darfst bigint und number nicht in einer Arithmetik-Operation mischen:

ts no-mix.ts
const a: bigint = 100n;
const b: number = 1;

const x = a + b;
//        ~~~~~ Operator '+' cannot be applied to types
//              'bigint' and 'number'.

// Auch zur Laufzeit ein TypeError:
// "Cannot mix BigInt and other types, use explicit conversions"

// Korrekt: explizit konvertieren
const y = a + BigInt(b);   // 101n
const z = Number(a) + b;   // 101  (mit Präzisionsrisiko bei großen a)

Das ist eine bewusste Design-Entscheidung. Stille Konvertierung würde entweder Genauigkeit kosten (number-Seite) oder den Sinn von bigint untergraben.

Vergleiche zwischen bigint und number

Bei reinen Vergleichsoperatoren ist die Sprache toleranter, weil hier keine Genauigkeit verlorengeht:

ts comparisons.ts
console.log(100n < 101);    // true
console.log(100n > 99);     // true
console.log(100n <= 100);   // true

// Loose Equality: konvertiert vor dem Vergleich
console.log(100n == 100);   // true

// Strict Equality: Typ muss übereinstimmen
console.log(100n === 100);  // false  — bigint vs number
console.log(100n === 100n); // true
Output
true
true
true
true
false
true

Faustregel: relationale Operatoren (<, >, <=, >=, ==) dürfen mischen, === nicht. In striktem Code arbeitest du ohnehin immer mit === und konvertierst vorher explizit.

Wann bigint, wann number?

bigint ist ein Spezialwerkzeug, kein Default. Konkret sinnvoll:

  • Kryptografie: Schlüssel, Modulo-Operationen, RSA-Berechnungen — alles weit jenseits von 2^53.
  • Datenbank-IDs: PostgreSQL bigint (64-bit signed), Twitter-Snowflake-IDs, Discord-Snowflakes. Wer solche IDs als number deserialisiert, riskiert kaputte Werte bei großen Konten.
  • Timestamps in Nanosekunden: process.hrtime.bigint() in Node.js liefert direkt einen bigint, weil Mikrosekunden-Tracking sonst überläuft.
  • Finance mit Cents als Integer: Beträge in der kleinsten Währungseinheit speichern und erst beim Anzeigen formatieren — bigint vermeidet Float-Drift komplett.
  • Bit-Manipulation auf 64-bit-Werten: &, |, ^, <<, >> funktionieren mit bigint, ohne dass der 32-bit-Cap zuschlägt.

Für alles andere — Counter, Indexe, Maße, Prozente, Pixel — bleibt number die richtige Wahl. bigint hat einen messbaren Performance-Nachteil, weil die Werte nicht in einem CPU-Register liegen.

Interessantes

JSON.stringify mit bigint wirft TypeError.

Ein nackter Aufruf wie JSON.stringify({ id: 100n }) bricht mit TypeError: Do not know how to serialize a BigInt ab. Workaround: einen Replacer übergeben, der bigint zu String wandelt — oder direkt am Layer-Übergang konvertieren. Viele HTTP-APIs liefern bigint-Werte deshalb als JSON-String zurück, nicht als Zahl.

BigInt(0.5) wirft RangeError.

Floats lassen sich nicht in bigint konvertieren. BigInt(0.5), BigInt(NaN) und BigInt(Infinity) sind alle Laufzeitfehler. Wer aus einer number-Rechnung einen bigint braucht, muss vorher mit Math.trunc oder Math.floor auf eine Ganzzahl runden.

Performance: bigint ist deutlich langsamer als number.

Operationen auf bigint laufen über Heap-allokierte Objekte mit variabler Länge, nicht in CPU-Registern. Für Hot-Loop-Berechnungen mit Werten unter 2^53 ist number oft eine Größenordnung schneller. Benchmarke, bevor du bigint flächendeckend einsetzt.

bigint kennt kein Infinity und kein NaN.

Diese Werte gehören zu IEEE-754. bigint ist eine reine Integer-Welt: jeder gültige bigint ist eine endliche Ganzzahl. Division durch 0n wirft daher RangeError: Division by zero — statt Infinity zurückzugeben wie bei number.

Division schneidet Richtung Null ab.

5n / 2n ergibt 2n, nicht 2.5n und nicht 3n. Genauso wie in C oder Go: Integer-Division, Rest geht verloren. Wer den Rest braucht, kombiniert mit %; wer Kommazahlen will, muss vorher zu number konvertieren — mit allen Präzisionsrisiken.

TypeScript 5.0+ und isolatedModules.

Im isolatedModules-Modus (Standard bei Bundlern wie esbuild, swc, Vite) prüft TypeScript jede Datei isoliert. bigint-Literale funktionieren auch hier ohne Sonderbehandlung, weil sie reine ECMAScript-Syntax sind — anders als z. B. const enum, das in diesem Modus problematisch wird.

Verfügbar in allen modernen Engines.

bigint ist seit Chrome 67, Firefox 68, Safari 14 und Node.js 10.4 verfügbar — also Baseline seit September 2020. Für Browser-Code ohne Legacy-Anforderungen kannst du bigint heute bedenkenlos verwenden, sofern target: es2020 in der tsconfig.json steht.

bigint in API-Responses meist als String kodiert.

Weil JSON keinen bigint kennt (siehe oben) und Floats keine 64-bit-IDs verlustfrei tragen, geben viele Backends bigint-Werte als String zurück: { id: "9007199254740993" }. Im Frontend dann mit BigInt(json.id) rekonstruieren — oder als String belassen, falls du nur damit rendern willst.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Primitive Typen

Zur Übersicht