hash.ts
Overview
The hash.ts file implements a stable hashing utility designed to generate consistent, unique string representations (hashes) for arbitrary JavaScript values, including complex objects, arrays, and special types like Dates and RegExps. Unlike typical serialization (e.g., JSON.stringify), this hash function:
Is fast with near O(1) lookup complexity for previously hashed objects via
WeakMap.Handles unserializable values and circular references gracefully.
Produces stable hashes by sorting object keys to ensure order-invariant results.
Generates short, readable hash strings.
Is not a reversible serialization, but a unique identifier useful for memoization, caching, or comparison.
This utility is especially suitable when you need to quickly generate unique keys for complex, nested data structures in memory while avoiding performance bottlenecks and memory leaks.
Detailed Explanations
Imported utilities
OBJECT: Imported from'./shared', this is presumably a reference toObjector a similar utility object.isUndefined: A utility function to check if a value isundefined.
Constants and Variables
table: WeakMap<object, number | string>A
WeakMapthat stores mappings from objects to their generated hash strings or temporary IDs. UsingWeakMapallows objects to be garbage collected when no longer referenced elsewhere, preventing memory leaks.counter: numberA numeric counter used to assign temporary unique IDs to objects during hashing, particularly to detect and handle circular references.
Helper Functions
getTypeName(value: any): string
Returns the internal
[[Class]]type string of a value, e.g.,[object Object],[object Date].Uses
OBJECT.prototype.toString.call(value).
Usage example:
getTypeName(new Date()); // '[object Date]'
getTypeName({}); // '[object Object]'
isObjectTypeName(typeName: string, type: string): boolean
Checks if a given
typeNamestring matches the expected object type, e.g., checks iftypeName === '[object Date]'.Used to distinguish between plain objects, dates, regexes, etc.
Usage example:
isObjectTypeName('[object Date]', 'Date'); // true
isObjectTypeName('[object Array]', 'Object'); // false
Main Exported Function
stableHash(arg: any): string
Generates a stable, unique hash string for the input value arg.
Parameters
arg: any— The input value to hash. Can be of any JavaScript type: primitives, arrays, objects, functions, dates, regexes, etc.
Returns
string— A stable hash string uniquely representing the input value.
Description and Algorithm
Type detection:
Determines the type ofargusingtypeofand internaltoStringchecks.Special handling for objects:
If
argis a non-null object/function (excluding Date and RegExp), the function:Checks if
arghas already been hashed by looking it up intable.If found, returns the cached hash string immediately (fast O(1) lookup).
Otherwise, assigns a temporary ID (
counterincremented plus'~') and stores it intableto detect circular references during recursion.If
argis an array, recursively hashes each element and concatenates results prefixed with'@'.If
argis a plain object, sorts its keys alphabetically, recursively hashes each key-value pair, and concatenates results prefixed with'#'.
Primitive and special types:
For Dates, uses
toJSON()to get a standardized string.For Symbols, uses
toString().For strings, JSON stringifies them.
For other primitives, converts to string.
Returns the resulting hash string.
Usage Examples
import { stableHash } from './hash'
const obj = { b: 2, a: 1 }
console.log(stableHash(obj)) // Output: '#a:1,b:2,' (keys sorted)
const arr = [1, 2, { x: 10 }]
console.log(stableHash(arr)) // Output: '@1,2,#x:10,'
const circular: any = {}
circular.self = circular
console.log(stableHash(circular)) // Handles circular reference gracefully
Important Implementation Details
WeakMap use:
The use ofWeakMapfor object-to-hash mapping is key to avoiding memory leaks and enabling O(1) hash retrieval for objects already processed.Circular Reference Detection:
Assigning a temporary ID before recursive calls allows the function to detect and safely handle circular references without infinite recursion.Stable Key Ordering:
Sorting object keys ensures that objects with the same keys and values but different insertion orders produce identical hashes.Hash Prefixes:
Special prefixes are used to distinguish types in the hash string:'#'for plain objects'@'for arrays'~'as temporary object ID during hashing
Non-serialization nature:
The result is not guaranteed to be JSON or parseable; it's a unique identifier, not a serialization.
Interaction with Other Parts of the System
Dependencies:
Imports from./shared(OBJECTandisUndefined) imply this file depends on shared utility functions or constants from elsewhere in the project.Usage scenarios:
This stable hashing function can be used in various parts of the system where unique keys for complex data are required, such as:Caching computed results (memoization)
Change detection in state management
Tracking object identities in collections
Deduplication of complex data structures
Garbage collection friendly:
Because it usesWeakMap, it integrates smoothly with the JavaScript engine's memory management, avoiding memory leaks in long-running applications.
Mermaid Diagram: Class and Function Structure
flowchart TD
A[stableHash(arg: any): string]
A --> B[getTypeName(value: any): string]
A --> C[isObjectTypeName(typeName: string, type: string): boolean]
A --> D[table: WeakMap<object, string|number>]
A --> E[counter: number]
B --> F[OBJECT.prototype.toString.call(value)]
C --> G[Compare typeName to expected '[object Type]']
stableHashis the main function that depends on helper functionsgetTypeNameandisObjectTypeName.It also uses the
tableWeakMap andcountervariable for state management.
Summary
The hash.ts file provides a robust, efficient, and stable hashing mechanism for arbitrary JavaScript values. It is optimized for performance with object caching, handles complex and circular structures, and produces consistent outputs by sorting keys. This utility is essential for scenarios requiring reliable, quick identification of data values without relying on full serialization or risking memory leaks.