Last active
September 22, 2023 15:24
-
-
Save sebinsua/a5fe809e9e44f1b5e9f54ed3fa5d091d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import stringify from "fast-json-stable-stringify"; | |
type ImmutablePrimitive = | |
| undefined | |
| null | |
| boolean | |
| string | |
| number | |
| Function; | |
export type Immutable<T> = T extends ImmutablePrimitive | |
? T | |
: T extends Array<infer U> | |
? ImmutableArray<U> | |
: T extends Map<infer K, infer V> | |
? ImmutableMap<K, V> | |
: T extends Set<infer M> | |
? ImmutableSet<M> | |
: ImmutableObject<T>; | |
export type ImmutableArray<T> = ReadonlyArray<Immutable<T>>; | |
export type ImmutableMap<K, V> = ReadonlyMap<Immutable<K>, Immutable<V>>; | |
export type ImmutableSet<T> = ReadonlySet<Immutable<T>>; | |
export type ImmutableObject<T> = { readonly [K in keyof T]: Immutable<T[K]> }; | |
const ws = new WeakSet<any>(); | |
const m = new Map<string, any>(); | |
function v<Value>(value: Value): Immutable<Value> { | |
if (typeof value !== "object" || value === null) { | |
// @ts-ignore | |
return value; | |
} | |
if (ws.has(value)) { | |
// @ts-ignore | |
return value; | |
} | |
const key = stringify(value); | |
if (m.has(key)) { | |
return m.get(key); | |
} | |
ws.add(value); | |
m.set(key, value); | |
// @ts-ignore | |
return value; | |
} | |
const a = v({ count: 1 }); | |
const b = v({ count: 1 }); | |
if (a === b) { | |
console.log( | |
"When `a` and `b` have value equality we also ensure that they have reference equality." | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I had no idea this technique already exists. It's apparently known as "hash consing".
I've made a small modification to the code above to play nicer with GC.
See: https://codesandbox.io/s/fancy-bash-2rx528?file=/src/index.ts