TypeScript
Completed
-
Section 1: Getting Started
-
Section 2: Basic Types
-
Section 3: Functions and Objects
-
Section 4: Advanced Types
-
Section 5: Object-Oriented TypeScript
-
Section 6: Working with Modules
-
Section 7: Tooling and Practice
-
Section 8: Type-Level Programming
-
Section 9: TypeScript with Backends
-
Section 10: Testing Typed Code
-
Section 11: Data Validation with Types
-
Section 12: Practical Projects
-
Section 13: Compiler Internals
-
Section 14: Configuration Deep Dive
-
Section 15: Enums, Symbols, and Special Types
-
Section 16: Working with Async Code
-
Section 17: TypeScript and the DOM
-
Section 18: Advanced Generics Practice
-
Section 19: Working with Third-Party Types
-
Section 20: Monorepo and Large-Scale Practices
-
Section 21: Common Pitfalls and Best Practices
-
Section 22: Interview Practice
-
Section 23: Handbook: Narrowing In Depth
-
Section 24: Handbook: Object Types In Depth
-
Section 25: Handbook: Classes In Depth
-
Section 26: Handbook: Modules In Depth
-
Section 27: Handbook: Declaration Files In Depth
-
Section 28: JSX and Namespaces
-
Section 29: Compiler Configuration Reference
-
Section 30: More Practice Exercises
-
Section 31: Handbook: Everyday Types Deep Dive
-
Section 32: Utility Types Full Reference
-
Section 33: Decorators Reference
-
Section 34: Mixins and Advanced OOP Patterns
-
Section 35: Iterators and Generators Typing
-
Section 36: More Type-Level Programming Practice
-
Section 37: TypeScript Ecosystem Tools
-
Section 38: TypeScript with Testing Frameworks
-
Section 39: TypeScript for Library Authors
-
Section 40: More Interview Practice
-
Section 41: Handbook: Functions In Depth
-
Section 42: Handbook: Type Manipulation Deep Dive
-
Section 43: More Real-World Patterns
-
Section 44: TypeScript Release Notes Highlights
-
Section 45: Final Practice and Review
181: BigInt Support in TypeScript
I've seen this bug trip up plenty of developers, usually when they're integrating with a backend that uses 64-bit integers for database IDs or financial balances. You're writing your logic, everything looks fine in your local tests with small numbers, and then suddenly, in production, your IDs start "rounding off" or comparing as equal when they aren't.
// A common scenario: Processing large database IDs from an API
function checkIdMatch(storedId: number, providedId: number) {
return storedId === providedId;
}
const dbId = 9007199254740993; // Just slightly over Number.MAX_SAFE_INTEGER
const inputId = 9007199254740992;
console.log(checkIdMatch(dbId, inputId)); // Returns: true
Wait, what? These numbers are clearly different, but TypeScript (and JavaScript) tells us they are the same. This happens because the number type is a 64-bit float. Once you cross the "safe integer" threshold (Number.MAX_SAFE_INTEGER, which is 253 - 1), the precision drops. The engine literally can't tell the difference between those two massive integers anymore.
Breaking the Safe Integer Limit
To fix this, we need a type that doesn't rely on floating-point math. That's where bigint comes in. It's a primitive specifically designed to handle integers of arbitrary length. If you're dealing with 64-bit IDs from a SQL database or counting something that could realistically exceed 9 quadrillion, you cannot use number.
You can create a bigint by appending an n to the end of an integer literal, or by using the BigInt() constructor.
// The fixed version
function checkIdMatch(storedId: bigint, providedId: bigint) {
return storedId === providedId;
}
const dbId = 9007199254740993n; // Notice the 'n'
const inputId = 9007199254740992n;
console.log(checkIdMatch(dbId, inputId)); // Returns: false
Handling the BigInt Type Mismatch
Here is the part that usually frustrates people: bigint and number are completely different types. You cannot mix them in mathematical operations. If you try to add a number to a bigint, TypeScript will throw a compiler error, and the runtime will throw a TypeError.
const balance = 100n;
const fee = 5;
// ❌ This will crash: "Cannot mix BigInt and other types"
const total = balance - fee;
I've found the cleanest way to handle this is to be intentional about your conversions. If you know a value is a small constant, just make it a bigint literal. If it's coming from a variable, cast it explicitly.
const balance = 100n;
const fee = 5;
// ✅ Convert the number to BigInt first
const total = balance - BigInt(fee);
console.log(total); // 95n
One last tip: be careful with JSON.stringify(). By default, JSON doesn't know what a bigint is and will throw an error. You'll need to convert your bigints to strings before sending them over the wire, which is actually best practice anyway since most JSON parsers in other languages will treat large numbers as floats and introduce the same precision bugs we just fixed.
📋 Practical Task
Build a High-Precision Nano-Timestamp Converter
In high-frequency trading or distributed logging, timestamps are often tracked in nanoseconds (10-9 seconds), which easily exceeds the Number.MAX_SAFE_INTEGER limit.
Your Task: Create a function called convertNanoToMilli that takes a nanosecond timestamp as a bigint and returns the equivalent millisecond timestamp as a bigint.
- The function must accept a parameter of type
bigint. - It should divide the nanoseconds by 1,000,000 to get milliseconds.
- The function must return a
bigint. - Test your function with the value
1672531200000000000n(a realistic nanosecond timestamp).
Ensure you are using bigint literals and type annotations throughout to prevent any precision loss or type mismatch errors.
There are no comments for now.