Last active
July 21, 2023 14:40
-
-
Save sebinsua/9f8fb82ad59b5f5b38015d05dd40144b to your computer and use it in GitHub Desktop.
WIP after playing around in GPT.
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
type Constructor<T> = new (...args: any[]) => T; | |
type DefaultType<T> = T extends Constructor<infer U> ? U : T; | |
function defaultObject<V, K extends string | symbol = string | symbol>(Type: Constructor<V> | V): Record<K, DefaultType<V>> & Iterable<[K, DefaultType<V>]> { | |
const store = new Map<K, DefaultType<V>>(); | |
const iterable = { | |
*[Symbol.iterator] () { | |
for (let [key, value] of store) { | |
yield [key, value]; | |
} | |
} | |
}; | |
return new Proxy(iterable as Record<K, DefaultType<V>>, { | |
get(target: Record<K, DefaultType<V>>, property: K): DefaultType<V> { | |
if (!store.has(property)) { | |
if (typeof Type === 'function') { | |
store.set(property, new (Type as Constructor<V>)() as DefaultType<V>); | |
} else { | |
store.set(property, Type as DefaultType<V>); | |
} | |
} | |
return store.get(property)!; | |
}, | |
has(target: Record<K, DefaultType<V>>, property: K): boolean { | |
return store.has(property); | |
}, | |
ownKeys(): (string | symbol)[] { | |
return Array.from(store.keys()); | |
}, | |
deleteProperty(target: Record<K, DefaultType<V>>, property: K): boolean { | |
return store.delete(property); | |
}, | |
set(target: Record<K, DefaultType<V>>, property: K, value: DefaultType<V>): boolean { | |
if (typeof Type === 'function') { | |
if (!(value instanceof (Type as Constructor<V>))) { | |
throw new Error(`Value does not belong to the default type.`); | |
} | |
} else { | |
if (value !== Type) { | |
throw new Error(`Value does not match the default value.`); | |
} | |
} | |
store.set(property, value); | |
return true; | |
}, | |
getOwnPropertyDescriptor(target: Record<K, DefaultType<V>>, property: K) { | |
if (store.has(property)) { | |
return { | |
enumerable: true, | |
configurable: true, | |
value: store.get(property), | |
}; | |
} | |
return undefined; | |
} | |
}) as Record<K, DefaultType<V>> & Iterable<[K, DefaultType<V>]>; | |
} | |
// Usage with Array | |
const d = defaultObject(Array); | |
d['abc'].push('def'); | |
const abc = d['abc']; | |
console.log('abc' in d); // Outputs: true | |
for (const [key, value] of d) { | |
console.log(key, value); | |
} | |
delete d['abc']; | |
console.log('abc' in d); // Outputs: false | |
// Usage with Set | |
const s = defaultObject(Set); | |
s['abc'].add('def'); | |
console.log('abc' in s); // Outputs: true | |
for (const [key, value] of s) { | |
console.log(key, value); | |
} | |
delete s['abc']; | |
console.log('abc' in s); // Outputs: false | |
// Usage with number | |
const n = defaultObject(0); | |
n['abc']++; | |
console.log('abc' in n); // Outputs: true | |
for (const [key, value] of n) { | |
console.log(key, value); | |
} | |
delete n['abc']; | |
console.log('abc' in n); // Outputs: false | |
// Usage with type checking on set | |
try { | |
d['abc'] = 'def'; // Throws an error | |
} catch (err) { | |
console.error(err); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment