typescript
32 lines · 8 steps
Formatting byte counts into human-readable sizes
A TypeScript helper that converts raw byte counts into readable strings like 1.5 MB, with decimal or binary units.
Explained by
highlit
1export interface FormatBytesOptions {
2 decimals?: number;
3 binary?: boolean;
4}
5
6export function formatBytes(
7 bytes: number,
8 { decimals = 1, binary = false }: FormatBytesOptions = {},
9): string {
10 if (!Number.isFinite(bytes) || bytes < 0) {
11 throw new RangeError(`Invalid byte value: ${bytes}`);
12 }
13
14 const base = binary ? 1024 : 1000;
15 const units = binary
16 ? ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"]
17 : ["B", "KB", "MB", "GB", "TB", "PB", "EB"];
18
19 if (bytes < base) {
20 return `${bytes} ${units[0]}`;
21 }
22
23 const exponent = Math.min(
24 Math.floor(Math.log(bytes) / Math.log(base)),
25 units.length - 1,
26 );
27
28 const value = bytes / base ** exponent;
29 const rounded = Number(value.toFixed(decimals));
30
31 return `${rounded} ${units[exponent]}`;
32}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A logarithm against the chosen base tells you directly which unit magnitude a number falls into.
- 2Destructuring with defaults on an options object keeps a function flexible without sprawling parameter lists.
- 3Validating inputs up front turns silent nonsense output into a clear, catchable error.
Related explainers
typescript
import { registerLocaleData } from '@angular/common'; import localeFr from '@angular/common/locales/fr'; import localeFrExtra from '@angular/common/locales/extra/fr'; import localeDe from '@angular/common/locales/de';
Locale-aware bootstrapping in Angular
i18n
localization
dependency-injection
Intermediate
8 steps
typescript
import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import * as Joi from 'joi';
Validating env config at boot in NestJS
configuration
schema-validation
environment-variables
Intermediate
8 steps
typescript
import { Inject, Injectable, Logger } from '@nestjs/common'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { Cache } from 'cache-manager'; import { InjectRepository } from '@nestjs/typeorm';
A cache-aside country lookup in NestJS
cache-aside
dependency-injection
batch-lookup
Intermediate
8 steps
typescript
import { Injectable, effect, signal, computed } from '@angular/core'; interface Preferences { theme: 'light' | 'dark';
A signal-based preferences store in Angular
signals
state-management
persistence
Intermediate
7 steps
typescript
import { useEffect, useState } from "react"; interface Section { id: string;
Building a scroll-spy hook in React
custom-hooks
intersectionobserver
dom-observation
Intermediate
8 steps
typescript
import { Injectable, Scope, Inject, NotFoundException } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; import { Request } from 'express'; import { DataSource } from 'typeorm';
Per-tenant database connections in NestJS
multi-tenancy
connection-pooling
dependency-injection
Advanced
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/formatting-byte-counts-into-human-readable-sizes-explained-typescript-4262/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.