diff options
author | Luca Casonato <hello@lcas.dev> | 2023-03-30 22:52:31 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-30 22:52:31 +0200 |
commit | 02e01b171f29f4f6c23d738b0756b7d9b7eaa020 (patch) | |
tree | 5f6b54bf84c6f0f330cf6519846a8e65ae5a0d9d | |
parent | e888c3f534c6ce9574f1d57e5cc61573a046039e (diff) |
fix(dts): improve types for the Deno.KV API (#18510)
-rw-r--r-- | cli/tests/unit/kv_test.ts | 50 | ||||
-rw-r--r-- | cli/tsc/dts/lib.deno.unstable.d.ts | 56 | ||||
-rw-r--r-- | ext/kv/01_db.ts | 18 |
3 files changed, 91 insertions, 33 deletions
diff --git a/cli/tests/unit/kv_test.ts b/cli/tests/unit/kv_test.ts index 401eba9b6..69eb8bed9 100644 --- a/cli/tests/unit/kv_test.ts +++ b/cli/tests/unit/kv_test.ts @@ -6,6 +6,7 @@ import { assertRejects, assertThrows, } from "./test_util.ts"; +import { assertType, IsExact } from "../../../test_util/std/testing/types.ts"; let isCI: boolean; try { @@ -529,8 +530,10 @@ Deno.test("KvU64 unbox", () => { assertEquals(a.value, 1n); }); -async function collect(iter: Deno.KvListIterator): Promise<Deno.KvEntry[]> { - const entries: Deno.KvEntry[] = []; +async function collect<T>( + iter: Deno.KvListIterator<T>, +): Promise<Deno.KvEntry<T>[]> { + const entries: Deno.KvEntry<T>[] = []; for await (const entry of iter) { entries.push(entry); } @@ -1134,3 +1137,46 @@ dbTest("operation size limit", async (db) => { "too many mutations (max 10)", ); }); + +// This function is never called, it is just used to check that all the types +// are behaving as expected. +async function _typeCheckingTests() { + const kv = new Deno.Kv(); + + const a = await kv.get(["a"]); + assertType<IsExact<typeof a, Deno.KvEntryMaybe<unknown>>>(true); + + const b = await kv.get<string>(["b"]); + assertType<IsExact<typeof b, Deno.KvEntryMaybe<string>>>(true); + + const c = await kv.getMany([["a"], ["b"]]); + assertType< + IsExact<typeof c, [Deno.KvEntryMaybe<unknown>, Deno.KvEntryMaybe<unknown>]> + >(true); + + const d = await kv.getMany([["a"], ["b"]] as const); + assertType< + IsExact<typeof d, [Deno.KvEntryMaybe<unknown>, Deno.KvEntryMaybe<unknown>]> + >(true); + + const e = await kv.getMany<[string, number]>([["a"], ["b"]]); + assertType< + IsExact<typeof e, [Deno.KvEntryMaybe<string>, Deno.KvEntryMaybe<number>]> + >(true); + + const keys: Deno.KvKey[] = [["a"], ["b"]]; + const f = await kv.getMany(keys); + assertType<IsExact<typeof f, Deno.KvEntryMaybe<unknown>[]>>(true); + + const g = kv.list({ prefix: ["a"] }); + assertType<IsExact<typeof g, Deno.KvListIterator<unknown>>>(true); + const h = await g.next(); + assert(!h.done); + assertType<IsExact<typeof h.value, Deno.KvEntry<unknown>>>(true); + + const i = kv.list<string>({ prefix: ["a"] }); + assertType<IsExact<typeof i, Deno.KvListIterator<string>>>(true); + const j = await i.next(); + assert(!j.done); + assertType<IsExact<typeof j.value, Deno.KvEntry<string>>>(true); +} diff --git a/cli/tsc/dts/lib.deno.unstable.d.ts b/cli/tsc/dts/lib.deno.unstable.d.ts index f4dab5663..b82a80382 100644 --- a/cli/tsc/dts/lib.deno.unstable.d.ts +++ b/cli/tsc/dts/lib.deno.unstable.d.ts @@ -1661,7 +1661,7 @@ declare namespace Deno { * * @category KV */ - export class KvListIterator implements AsyncIterableIterator<KvEntry> { + export class KvListIterator<T> implements AsyncIterableIterator<KvEntry<T>> { /** * Returns the cursor of the current position in the iteration. This cursor * can be used to resume the iteration from the current position in the @@ -1669,8 +1669,8 @@ declare namespace Deno { */ get cursor(): string; - next(): Promise<IteratorResult<KvEntry, any>>; - [Symbol.asyncIterator](): AsyncIterableIterator<KvEntry>; + next(): Promise<IteratorResult<KvEntry<T>, undefined>>; + [Symbol.asyncIterator](): AsyncIterableIterator<KvEntry<T>>; } /** **UNSTABLE**: New API, yet to be vetted. @@ -1680,16 +1680,26 @@ declare namespace Deno { * The `versionstamp` is a string that represents the current version of the * key-value pair. It can be used to perform atomic operations on the KV store * by passing it to the `check` method of a {@linkcode Deno.AtomicOperation}. - * A `null` versionstamp indicates that no value exists for the given key in - * the KV store. * * @category KV */ - export interface KvEntry { + export type KvEntry<T> = { key: KvKey; value: T; versionstamp: string }; + + /** + * **UNSTABLE**: New API, yet to be vetted. + * + * An optional versioned pair of key and value in a {@linkcode Deno.Kv}. + * + * This is the same as a {@linkcode KvEntry}, but the `value` and `versionstamp` + * fields may be `null` if no value exists for the given key in the KV store. + * + * @category KV + */ + export type KvEntryMaybe<T> = KvEntry<T> | { key: KvKey; - value: unknown; - versionstamp: string | null; - } + value: null; + versionstamp: null; + }; /** **UNSTABLE**: New API, yet to be vetted. * @@ -1881,8 +1891,8 @@ declare namespace Deno { export class Kv { /** * Retrieve the value and versionstamp for the given key from the database - * in the form of a {@linkcode Deno.KvEntry}. If no value exists for the key, - * the returned entry will have a `null` value and versionstamp. + * in the form of a {@linkcode Deno.KvEntryMaybe}. If no value exists for + * the key, the returned entry will have a `null` value and versionstamp. * * ```ts * const db = await Deno.openKv(); @@ -1898,17 +1908,17 @@ declare namespace Deno { * information on consistency levels, see the documentation for * {@linkcode Deno.KvConsistencyLevel}. */ - get( + get<T = unknown>( key: KvKey, options?: { consistency?: KvConsistencyLevel }, - ): Promise<KvEntry>; + ): Promise<KvEntryMaybe<T>>; /** * Retrieve multiple values and versionstamps from the database in the form - * of an array of {@linkcode Deno.KvEntry} objects. The returned array will - * have the same length as the `keys` array, and the entries will be in the - * same order as the keys. If no value exists for a given key, the returned - * entry will have a `null` value and versionstamp. + * of an array of {@linkcode Deno.KvEntryMaybe} objects. The returned array + * will have the same length as the `keys` array, and the entries will be in + * the same order as the keys. If no value exists for a given key, the + * returned entry will have a `null` value and versionstamp. * * ```ts * const db = await Deno.openKv(); @@ -1927,11 +1937,10 @@ declare namespace Deno { * information on consistency levels, see the documentation for * {@linkcode Deno.KvConsistencyLevel}. */ - getMany( - keys: KvKey[], + getMany<T extends readonly unknown[]>( + keys: readonly [...{ [K in keyof T]: KvKey }], options?: { consistency?: KvConsistencyLevel }, - ): Promise<KvEntry[]>; - + ): Promise<{ [K in keyof T]: KvEntryMaybe<T[K]> }>; /** * Set the value for the given key in the database. If a value already * exists for the key, it will be overwritten. @@ -1993,7 +2002,10 @@ declare namespace Deno { * list operation. See the documentation for {@linkcode Deno.KvListOptions} * for more information. */ - list(selector: KvListSelector, options?: KvListOptions): KvListIterator; + list<T = unknown>( + selector: KvListSelector, + options?: KvListOptions, + ): KvListIterator<T>; /** * Create a new {@linkcode Deno.AtomicOperation} object which can be used to diff --git a/ext/kv/01_db.ts b/ext/kv/01_db.ts index e0c5335e6..05e9a66d8 100644 --- a/ext/kv/01_db.ts +++ b/ext/kv/01_db.ts @@ -75,7 +75,7 @@ class Kv { async getMany( keys: Deno.KvKey[], opts?: { consistency?: Deno.KvConsistencyLevel }, - ): Promise<Deno.KvEntry[]> { + ): Promise<Deno.KvEntry<unknown>[]> { keys = keys.map(convertKey); const ranges: RawKvEntry[][] = await core.opAsync( "op_kv_snapshot_read", @@ -174,7 +174,7 @@ class Kv { cursor: string | undefined, reverse: boolean, consistency: Deno.KvConsistencyLevel, - ) => Promise<Deno.KvEntry[]> { + ) => Promise<Deno.KvEntry<unknown>[]> { return async (selector, cursor, reverse, consistency) => { const [entries]: [RawKvEntry[]] = await core.opAsync( "op_kv_snapshot_read", @@ -304,7 +304,7 @@ function convertKey(key: Deno.KvKey | Deno.KvKeyPart): Deno.KvKey { } } -function deserializeValue(entry: RawKvEntry): Deno.KvEntry { +function deserializeValue(entry: RawKvEntry): Deno.KvEntry<unknown> { const { kind, value } = entry.value; switch (kind) { case "v8": @@ -357,9 +357,9 @@ const AsyncIteratorPrototype = ObjectGetPrototypeOf(AsyncGeneratorPrototype); const AsyncIterator = AsyncIteratorPrototype.constructor; class KvListIterator extends AsyncIterator - implements AsyncIterator<Deno.KvEntry> { + implements AsyncIterator<Deno.KvEntry<unknown>> { #selector: Deno.KvListSelector; - #entries: Deno.KvEntry[] | null = null; + #entries: Deno.KvEntry<unknown>[] | null = null; #cursorGen: (() => string) | null = null; #done = false; #lastBatch = false; @@ -368,7 +368,7 @@ class KvListIterator extends AsyncIterator cursor: string | undefined, reverse: boolean, consistency: Deno.KvConsistencyLevel, - ) => Promise<Deno.KvEntry[]>; + ) => Promise<Deno.KvEntry<unknown>[]>; #limit: number | undefined; #count = 0; #reverse: boolean; @@ -388,7 +388,7 @@ class KvListIterator extends AsyncIterator cursor: string | undefined, reverse: boolean, consistency: Deno.KvConsistencyLevel, - ) => Promise<Deno.KvEntry[]>; + ) => Promise<Deno.KvEntry<unknown>[]>; }, ) { super(); @@ -443,7 +443,7 @@ class KvListIterator extends AsyncIterator return this.#cursorGen(); } - async next(): Promise<IteratorResult<Deno.KvEntry>> { + async next(): Promise<IteratorResult<Deno.KvEntry<unknown>>> { // Fused or limit exceeded if ( this.#done || @@ -493,7 +493,7 @@ class KvListIterator extends AsyncIterator }; } - [Symbol.asyncIterator](): AsyncIterator<Deno.KvEntry> { + [Symbol.asyncIterator](): AsyncIterator<Deno.KvEntry<unknown>> { return this; } } |