A set of runtime-agnostic tools to deal with binary encoding and decoding in TypeScript.
This library allows manual handling of bytes or to declarativelly write binary layouts descriptions that automatically:
- Encodes JavaScript objects →
Uint8Array - Decodes
Uint8Array→ strongly‑typed objects - Works at bit‑level or byte‑level precision
- Allows configuration of in/out endianness
- Shares the same codebase across Bun, Deno and Node.js
No runtime‑specific APIs are required beyond standard Web/JS primitives.
As a source‑first library, just copy the Bytes.ts file to your project folder and then import:
import { Bytes } from "./Bytes.ts";All the functions that deal with multiple bytes defaults its input and output endianess to little endian, but are configurable by arguments of type:
type Endianness = "LITTLE" | "BIG";For use in this libary, binary types are represented as string literals:
type TypeAsString =
| "bool"
| "byte"
| "int"
| "float"
| "double"
| "string"
| "array"
| "struct";They map to concrete TypeScript types:
| Binary Type | JS Type |
|---|---|
bool |
boolean |
byte |
number (0–255) |
int |
number (int32) |
float |
number (float32) |
double |
number (float64) |
string |
string |
array |
Array<T> |
struct |
object |
Byte <-> value conversion helpers.
Signature:
<T extends PrimitiveAsString>(
type: T,
out_endianness: Endianness = `LITTLE`
) => (value: PrimitiveTypeReference[T]) => Uint8ArrayEncodes a value into a Uint8Array.
const encodeInt = Bytes.from("int");
const buf = encodeInt(123);Supported types:
type PrimitiveAsString =
| "bool"
| "byte"
| "int"
| "float"
| "double"
| "string";Signature:
<T extends PrimitiveAsString>(
type: T,
in_endianness: Endianness = `LITTLE`
) => (value: Uint8Array) => PrimitiveTypeReference[T] | voidDecodes a Uint8Array into a JS value.
const decodeInt = Bytes.to("int");
const value = decodeInt(buf);Signature:
(
size: number,
in_endianness: Endianness = `LITTLE`,
out_endianness: Endianness = `LITTLE`
) => (value: Uint8Array) => Uint8ArrayPads a Uint8Array to a fixed size.
const pad8 = Bytes.padding(8);
const padded = pad8(new Uint8Array([1, 2]));Signature:
(value: Uint8Array) => stringEncodes binary data to Base64.
const b64 = Bytes.toBase64(buf);Signature:
(
offset: number,
bits: number,
in_endianness: Endianness = `LITTLE`,
out_endianness: Endianness = `LITTLE`
) => (value: Uint8Array) => Uint8ArrayExtracts a bit‑range from a Uint8Array.
const slice = Bytes.reframe(3, 5)(buffer);offset→ starting bitbits→ number of bits to extract
Result is right‑aligned and byte‑packed.
Signature:
(buff: Uint8Array) => numberC‑style string length detection (null‑terminated).
const len = Bytes.strlen(buffer);A description of a single binary field.
type DecodeDescription = {
name: string;
} & (
| { type: "bool"; bits?: number; bytes?: number }
| { type: "byte" | "int" | "float" | "double"; bits?: number; bytes?: number }
| { type: "string"; bytes?: number }
| { type: "array"; value_description: DecodeDescription; size: number }
| { type: "struct"; description: DecodeDescription[] }
);Example description:
const Packet = {
name: "packet",
type: "struct",
description: [
{ name: "id", type: "int", bytes: 4 },
{ name: "temperature", type: "float", bytes: 4 },
{ name: "valid", type: "bool", bits: 1 },
],
} as const;Automatically infers the runtime object type from a binary description.
type PacketType = DescribedType<typeof Packet>;
// {
// id: number;
// temperature: number;
// valid: boolean;
// }Signature:
(d: DecodeDescription, value?: Uint8Array) => numberComputes the minimum required size in bytes.
const size = Bytes.size_in_memory(Packet);Works recursively for arrays and structs.
Signature:
<T extends DecodeDescription>(
desc: T,
out_endianness: Endianness = `LITTLE`
) => (value: DescribedType<T>) => Uint8ArrayReturns a function that encodes structured data into binary.
const encode = Bytes.encoder(Packet);
const buf = encode({ id: 1, temperature: 36.5, valid: true });Signature:
<T extends DecodeDescription>(
desc: T,
in_endianness: Endianness = `LITTLE`
) => (value: Uint8Array) => DescribedType<T>Returns a function that decodes binary data into structured objects.
const decode = Bytes.decoder(Packet);
const value = decode(buf);Arrays and structs can be nested arbitrarily.
const Shape = {
name: "shape",
type: "struct",
description: [
{
name: "points",
type: "array",
size: 2,
value_description: {
name: "p",
type: "struct",
description: [
{ name: "x", type: "float", bytes: 4 },
{ name: "y", type: "float", bytes: 4 },
],
},
},
],
} as const;- Bun
- Deno
- Node
Relies only on:
Uint8ArrayArrayBufferDataViewTextEncoder/TextDecoder
- better methods explanations
- encoding/decoding not byte aligned structures