> ## Documentation Index
> Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# TypeScript Patterns

> Branded types, namespace exports, dual APIs, and file organization

# TypeScript Patterns

Voltaire uses specific TypeScript patterns for type safety, tree-shaking, and API consistency.

## Branded Types

All primitives are branded `Uint8Array`s - zero runtime overhead, full type safety.

```typescript theme={null}
// AddressType.ts
declare const brand: unique symbol;

export type AddressType = Uint8Array & {
  readonly [brand]: "Address";
  readonly length: 20;
};
```

### Why Branded Types?

1. **Type Safety**: Can't accidentally pass `Hash` where `Address` expected
2. **Zero Overhead**: Just TypeScript - no runtime checks
3. **Uint8Array Base**: Works with all binary APIs natively
4. **Self-Documenting**: Types describe exact byte lengths

```typescript theme={null}
// Type system catches errors at compile time
function transfer(to: Address, amount: Uint256): void { ... }

const hash = Hash.fromHex("0x...");  // 32 bytes
transfer(hash, amount);  // ❌ Type error: Hash is not Address
```

## File Organization

Each primitive follows this structure:

```
Address/
├── AddressType.ts         # Type definition only
├── from.js                # Constructor (no wrapper needed)
├── toHex.js               # Internal method
├── equals.js              # Internal method
├── isValid.js             # Validation
├── index.ts               # Dual exports + wrappers
└── Address.test.ts        # Tests
```

### Why .js for Implementation?

Implementation files use `.js` with JSDoc types:

```javascript theme={null}
// toHex.js
/**
 * @param {import('./AddressType.js').AddressType} address
 * @returns {import('../Hex/HexType.js').Hex}
 */
export function toHex(address) {
  return Hex.fromBytes(address);
}
```

Benefits:

* No compilation step for runtime code
* JSDoc provides type checking
* Better tree-shaking
* Faster builds

## Namespace Pattern

Functions are exported both internally and wrapped:

```typescript theme={null}
// index.ts

// Internal export (underscore prefix)
export { toHex as _toHex } from "./toHex.js";
export { equals as _equals } from "./equals.js";

// Public wrapper with auto-conversion
export function toHex(value: AddressInput): Hex {
  return _toHex(from(value));
}

export function equals(a: AddressInput, b: AddressInput): boolean {
  return _equals(from(a), from(b));
}
```

### Usage

```typescript theme={null}
import * as Address from "@voltaire/primitives/Address";

// Public API - accepts various inputs
const hex = Address.toHex("0x742d35Cc6634C0532925a3b844Bc9e7595f251e3");

// Internal API - requires branded type, no conversion overhead
const addr = Address.from("0x742d35Cc6634C0532925a3b844Bc9e7595f251e3");
const hex2 = Address._toHex(addr);
```

### Why Dual Exports?

| Export     | Use Case                             | Performance         |
| ---------- | ------------------------------------ | ------------------- |
| `toHex()`  | General usage                        | Conversion overhead |
| `_toHex()` | Hot paths, already have branded type | Zero overhead       |

## Constructor Pattern

### Main Constructor

The primary constructor is just the type name:

```typescript theme={null}
// ✅ Preferred
const addr = Address("0x742d...");
const hash = Hash("0xabc...");
const uint = Uint256(42n);

// ❌ Avoid
const addr = Address.from("0x742d...");  // More verbose
```

### Named Constructors

For specific input types:

```typescript theme={null}
// From specific formats
const addr1 = Address.fromHex("0x742d35Cc6634C0532925a3b844Bc9e7595f251e3");
const addr2 = Address.fromBytes(bytes);
const addr3 = Address.fromPublicKey(pubkey);

// To specific formats
const hex = Address.toHex(addr);
const bytes = Address.toBytes(addr);
const checksummed = Address.toChecksummed(addr);
```

## Type Narrowing

### Input Types

Define input types for flexible function signatures:

```typescript theme={null}
// types.ts
export type AddressInput =
  | AddressType           // Already branded
  | Hex                   // Hex string
  | Uint8Array            // Raw bytes
  | `0x${string}`;        // Literal hex
```

### The `from` Function

Central converter that handles all inputs:

```typescript theme={null}
// from.js
export function from(value: AddressInput): AddressType {
  if (isAddress(value)) return value;          // Already branded
  if (typeof value === "string") return fromHex(value);
  if (value instanceof Uint8Array) return fromBytes(value);
  throw new Error(`Invalid address input: ${value}`);
}
```

## Validation Pattern

### Type Guards

```typescript theme={null}
// is.js
export function is(value: unknown): value is AddressType {
  return value instanceof Uint8Array &&
         value.length === 20 &&
         hasAddressBrand(value);
}
```

### Validation Functions

```typescript theme={null}
// isValid.js
export function isValid(value: unknown): boolean {
  if (typeof value === "string") {
    return /^0x[0-9a-fA-F]{40}$/.test(value);
  }
  if (value instanceof Uint8Array) {
    return value.length === 20;
  }
  return false;
}
```

## Error Handling

### Custom Errors

```typescript theme={null}
// errors.ts
export class InvalidAddressError extends Error {
  readonly name = "InvalidAddressError";

  constructor(value: unknown) {
    super(`Invalid address: ${String(value).slice(0, 100)}`);
  }
}
```

### Usage in Functions

```typescript theme={null}
export function fromHex(hex: string): AddressType {
  if (!/^0x[0-9a-fA-F]{40}$/.test(hex)) {
    throw new InvalidAddressError(hex);
  }
  // ... conversion logic
}
```

## Testing Pattern

Tests in separate `.test.ts` files using Vitest:

```typescript theme={null}
// Address.test.ts
import { describe, it, expect } from "vitest";
import * as Address from "./index.js";

describe("Address", () => {
  describe("fromHex", () => {
    it("converts valid lowercase hex", () => {
      const addr = Address.fromHex("0x742d35cc6634c0532925a3b844bc9e7595f251e3");
      expect(addr).toBeInstanceOf(Uint8Array);
      expect(addr.length).toBe(20);
    });

    it("throws on invalid hex", () => {
      expect(() => Address.fromHex("0xinvalid")).toThrow();
    });
  });

  describe("toChecksummed", () => {
    it("applies EIP-55 checksum", () => {
      const addr = Address.fromHex("0x742d35cc6634c0532925a3b844bc9e7595f251e3");
      expect(Address.toChecksummed(addr)).toBe("0x742d35Cc6634C0532925a3b844Bc9e7595f251e3");
    });
  });
});
```

## Index File Template

Complete `index.ts` structure:

```typescript theme={null}
// Re-export type
export type { AddressType } from "./AddressType.js";
export type { AddressType as Address } from "./AddressType.js";

// Constructor (no wrapper needed)
export { from } from "./from.js";
export { from as Address } from "./from.js";

// Named constructors
export { fromHex } from "./fromHex.js";
export { fromBytes } from "./fromBytes.js";

// Internal methods (underscore prefix)
export { toHex as _toHex } from "./toHex.js";
export { toBytes as _toBytes } from "./toBytes.js";
export { equals as _equals } from "./equals.js";

// Public wrappers
import { from } from "./from.js";
import { toHex as _toHex } from "./toHex.js";
import type { AddressInput } from "./types.js";

export function toHex(value: AddressInput): Hex {
  return _toHex(from(value));
}

// Validation (no wrapper needed - handles any input)
export { isValid } from "./isValid.js";
export { is } from "./is.js";

// Constants
export { ZERO_ADDRESS } from "./constants.js";
```

## Common Mistakes

<Warning>
  Avoid these patterns:
</Warning>

```typescript theme={null}
// ❌ Using .ts for implementations
// toHex.ts - requires compilation, worse tree-shaking

// ❌ Missing internal exports
export { toHex } from "./toHex.js";  // No _toHex variant

// ❌ Wrapper without from() call
export function toHex(value: AddressInput): Hex {
  return _toHex(value);  // Wrong - value might not be branded
}

// ❌ Class-based API
export class Address {
  // Voltaire uses namespace pattern, not classes
}
```
