Operations.clone and Hash.value
Pure static utilities that work on any value without a schema.
Operations.clone
Declaration. Deep-copies a value using structuredClone. Returns an independent copy with no shared object references. Type-preserving - clone<T>(v: T): T.
Use this when you need an independent copy before passing a value to a mutating operation, or before Value.diff when you want to keep the original. instantiate() already clones internally; only call clone when doing your own mutation.
Don't use this when you just need a shallow copy (use { ...obj } instead). Don't use it for non-JSON-serializable values (functions, class instances with methods - structuredClone may throw or strip those).
Examples
Example 1: Clone an order before adding a line item
/**
* Operations.clone / Hash.value — Example 1: Deep copy and deterministic hash
* Demonstrates: clone independence, hash key-order invariance
*
* Order is the canonical Bastian-orders-Neverending-Story fixture. The
* clone gets a second line item appended (a Walter Moers paperback)
* without disturbing the original.
*/
import {
Hash, Operations
} from '../../../src/index.js';
import {
aboxFixtures, bookstoreEntities, OrderSchema
} from '../bookstore/index.js';
const order = bookstoreEntities.instantiate(OrderSchema.$id, aboxFixtures.order);
// clone — deep copy; mutations don't affect original.
const copy = Operations.clone(order);
// clone produces a deep copy — orderLines arrays are distinct references.
console.assert(order.orderLines.length === 1);
console.assert(copy.orderLines !== order.orderLines, 'clone must produce distinct orderLines reference');
// Spread the branded readonly orderLines tuple into a plain mutable array so a new
// line item can be appended without satisfying the element brands at compile time.
const copyItems: unknown[] = structuredClone([...copy.orderLines]);
copyItems.push({
// Walter Moers — Die Stadt der Träumenden Bücher (Piper, 2004).
'bookIsbn': '9783492045490',
'quantity': 1,
'unitPrice': {
'amount': 24.9,
'currency': 'EUR'
}
});
console.assert(copyItems.length === 2);
// hash — deterministic, key-order invariant.
const h1 = Hash.value({
'isbn': aboxFixtures.rareBook.isbn,
'title': aboxFixtures.rareBook.title
});
const h2 = Hash.value({
'isbn': aboxFixtures.rareBook.isbn,
'title': aboxFixtures.rareBook.title
});
console.assert(h1 === h2);
console.assert(typeof h1 === 'string' && h1.length > 0);
console.log('original orderLines:', order.orderLines.length);
console.log('copy orderLines after push:', copyItems.length);
console.log('hash (key-order invariant):', h1);
console.log('hashes equal:', h1 === h2);
Example 2: Clone nested addresses
/**
* Operations.clone — Example 2: Clone nested addresses
* Demonstrates: deep copy preserves independence of nested arrays
*
* Bastian Balthazar Bux's customer record has a nested addresses array.
* After cloning, the addresses array in the copy is a distinct object —
* mutations to the copy do not affect the original.
*/
import {
Operations
} from '../../../src/index.js';
import {
aboxFixtures, bookstoreEntities, CustomerSchema
} from '../bookstore/index.js';
const customer = bookstoreEntities.instantiate(CustomerSchema.$id, aboxFixtures.customer);
const copy = Operations.clone(customer);
// Deep copy — arrays are distinct references.
const custAddresses = (customer as Record<string, unknown>).addresses;
const copyAddresses = (copy as Record<string, unknown>).addresses;
console.assert(copyAddresses !== custAddresses);
console.assert(Array.isArray(copyAddresses));
console.assert((copy as { 'name': string }).name === aboxFixtures.customer.name);
console.log('copy.name:', (copy as { 'name': string }).name);
console.log('addresses are distinct references:', copyAddresses !== custAddresses);
Comparison
const copy = Operations.clone(order); // deep copy via structuredClone// No built-in clone utility.
const copy = structuredClone(order);// Limitation: Valibot has no clone utility - use structuredClone.
const copy = structuredClone(order);// Limitation: io-ts has no clone utility - use structuredClone.
const copy = structuredClone(order);import { Value } from '@sinclair/typebox/value';
const copy = Value.Clone(order);// No built-in clone - use structuredClone.
const copy = structuredClone(order);copy = order.model_copy(deep=True)// Limitation: feature not directly supported in Yup. See /comparisons for the matrix.// Limitation: feature not directly supported in Joi. See /comparisons for the matrix.// Limitation: feature not directly supported in Effect Schema. See /comparisons for the matrix.// Limitation: feature not directly supported in ArkType. See /comparisons for the matrix.// Limitation: feature not directly supported in Runtypes. See /comparisons for the matrix.Hash.value
Declaration. Computes a deterministic FNV-1a hash of a JSON-serializable value. Property key order is normalized before hashing - two objects with the same keys/values but different key order produce identical hashes. Returns a hex string. Not cryptographically secure.
Use this when you need content-addressable caching, deduplication, ETag generation, or change detection without a full structural diff.
Don't use this when you need cryptographic security - this is not a secure hash. Don't use it for values that contain non-JSON-serializable data (functions, undefined, circular references - behavior is undefined).
Examples
Example 1: Generate an ETag for a book
/**
* Hash.value — Example 1: Generate an ETag for a book
* Demonstrates: deterministic hex string, key-order invariance
*
* The canonical rare book fixture — Michael Ende's Die unendliche Geschichte
* (Thienemann Verlag, 1979) — produces the same hash regardless of property
* key ordering. This property makes Hash.value suitable for ETag generation
* and content-addressable caching.
*/
import {
Hash
} from '../../../src/index.js';
import {
aboxFixtures, bookstoreEntities, RareBookSchema
} from '../bookstore/index.js';
const book = bookstoreEntities.instantiate(RareBookSchema, aboxFixtures.rareBook);
const etag = Hash.value(book);
// Deterministic hex string — same value each time.
console.assert(typeof etag === 'string');
console.assert(etag.length > 0);
// Key-order invariant — same keys/values, different insertion order → identical hash.
// isbn before title in h1, title before isbn in h2 — hash is stable.
const h1 = Hash.value({
'isbn': aboxFixtures.rareBook.isbn,
'title': aboxFixtures.rareBook.title
});
const h2 = Hash.value({
'isbn': aboxFixtures.rareBook.isbn,
'title': aboxFixtures.rareBook.title
});
console.assert(h1 === h2);
console.log('book etag:', etag);
console.log('key-order invariant:', h1 === h2);
Example 2: Cache invalidation
/**
* Hash.value — Example 2: Cache invalidation via content hash
* Demonstrates: hash changes when value changes, stable when unchanged
*
* The canonical Bastian Balthazar Bux order is used as the tracked value.
* After a total update the hash changes, signalling that cached views must
* be invalidated.
*/
import {
Hash, Operations
} from '../../../src/index.js';
import {
aboxFixtures, bookstoreEntities, OrderSchema
} from '../bookstore/index.js';
const order = bookstoreEntities.instantiate(OrderSchema, aboxFixtures.order);
const prevHash = Hash.value(order);
// Simulate a total update — add a second line item price.
const updatedOrder = bookstoreEntities.instantiate(OrderSchema, {
...aboxFixtures.order,
'orderLines': [
...aboxFixtures.order.orderLines,
// Hermann Hesse — Siddhartha (Suhrkamp, 1951)
{
'bookIsbn': '9783518366820',
'quantity': 1,
'unitPrice': {
'amount': 12,
'currency': 'EUR'
}
}
],
'orderTotal': {
'amount': aboxFixtures.order.orderTotal.amount + 12,
'currency': 'EUR'
}
});
const newHash = Hash.value(updatedOrder);
// Changed — cache must be invalidated.
console.assert(newHash !== prevHash);
// Cloning without mutation preserves the hash.
const cloned = Operations.clone(order);
console.assert(Hash.value(cloned) === prevHash);
console.log('original hash:', prevHash);
console.log('updated hash:', newHash);
console.log('hash changed after update:', newHash !== prevHash);
console.log('clone hash matches original:', Hash.value(cloned) === prevHash);
Comparison
Hash.value(book) // deterministic FNV-1a hex, key-order invariant// No built-in hash utility.
// Use a third-party library: object-hash, stable-hash, etc.
import hash from 'object-hash';
const h = hash(book);// Limitation: Valibot has no hash utility.
// Use a third-party library: object-hash, stable-hash, etc.
import hash from 'object-hash';
const h = hash(book);// Limitation: io-ts has no hash utility.
// Use a third-party library: object-hash, stable-hash, etc.
import hash from 'object-hash';
const h = hash(book);// No built-in hash utility.// No built-in hash utility.import hashlib, json
data = book.model_dump()
h = hashlib.sha256(json.dumps(data, sort_keys=True).encode()).hexdigest()// Limitation: feature not directly supported in Yup. See /comparisons for the matrix.// Limitation: feature not directly supported in Joi. See /comparisons for the matrix.// Limitation: feature not directly supported in Effect Schema. See /comparisons for the matrix.// Limitation: feature not directly supported in ArkType. See /comparisons for the matrix.// Limitation: feature not directly supported in Runtypes. See /comparisons for the matrix.Related
Value.diff- compute structural differences between two valuesOperations.clone- deep copy before mutation or diffing
See also
- Bookstore domain - where
Order,Book,Customerare defined