Last active
May 9, 2022 19:23
-
-
Save jRimbault/c69aecdb0e2e7dd6a554b034ea18a6ed 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
/** | |
* Every asserts stays true. | |
* | |
* This means Storage<{ key: number } & { foo: string }> and Storage<{}> can coexist. | |
* | |
* The first is an extension of the second. | |
* This is trivially true, but "wrong" for this use case. | |
* | |
* Is there a better way to achieve the same thing ? | |
*/ | |
interface Storage<Map extends Record<string, unknown> = {}> { | |
set<Key extends string, Value>( | |
key: Exclude<Key, keyof Map>, | |
value: Value | |
): asserts this is Storage<Map & { [k in Key]: Value }>; | |
get<Key extends keyof Map>(key: Key): Map[Key]; | |
// doesn't work | |
remove<Key extends string>( | |
key: Key | |
): asserts this is Storage<{ [K in Exclude<keyof Map, Key>]: Map[K] }>; | |
// doesn't work | |
clear(): asserts this is Storage<{}>; | |
} | |
declare const store: Storage; | |
{ | |
store.set("key", 1); | |
store.set("foo", ""); | |
store.set("bar", { hello: "world" }); | |
const a = store.get("key"); | |
const b = store.get("foo"); | |
store.remove("foo"); // doesn't work | |
store.clear(); // doesn't work | |
store.get("foo"); | |
const c = store.get("bar"); | |
} |
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
/** | |
* This could be solvable with an immutable design. | |
* | |
* But JS lacks the move semantics which would enforce correct usage. | |
* (and/or variable shadowing which would _help_ but not _enforce_) | |
*/ | |
interface ImmutableStorage<Map extends Record<string, unknown> = {}> { | |
set<Key extends string, Value>(key: Exclude<Key, keyof Map>, value: Value): | |
ImmutableStorage<Map & { [k in Key]: Value }>; | |
get<Key extends keyof Map>(key: Key): Map[Key]; | |
remove<Key extends string>(key: Key): | |
ImmutableStorage<{ [K in Exclude<keyof Map, Key>]: Map[K] }>; | |
clear(): ImmutableStorage<{}>; | |
} | |
declare const empty: ImmutableStorage; | |
{ | |
const one = empty.set("key", 1); | |
const two = one.set("foo", ""); | |
const three = two.set("bar", { hello: "world" }); | |
const a = three.get("key"); | |
const b = three.get("foo"); | |
const four = three.remove("foo"); | |
const five = four.clear(); | |
five.get("foo"); | |
const c = five.get("bar"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
One other note: you might want to pick a different name!
Storage
here is getting interface-merged with the globalStorage
interface for the web storage API! (playground) That's not the cause of the issue you're seeing here, but it is likely to troll you in other ways.