jakearchibald / idb

IndexedDB, but with promises
https://www.npmjs.com/package/idb
ISC License
6.2k stars 348 forks source link

Narrow types when an object store is declared as a union #291

Open indianakernick opened 1 year ago

indianakernick commented 1 year ago

This makes some changes to the type declarations so that the value type of an object store can be narrowed based on the key type (and vice versa).

interface Schema extends DBSchema {
  store: {
    key: 'foo';
    value: string;
  } | {
    key: 'bar';
    value: number;
  };
}

// Type of `foo` before this PR:
//   string | number | undefined
// Type of `foo` after this PR:
//   string | undefined
const foo = await db.get('store', 'foo');

// Both of these are fine before this PR but fail to compile after this PR.
await db.put('store', 'string', 'bar');
await db.put('store', 1234, 'foo');

There are some caveats. There's a limitation in the way that TypeScript unions work (see microsoft/TypeScript#18758). It means that the type changes made in this PR cannot be applied to indexes. It also means that narrowing the key type based on a value type when the value type is an object type isn't possible (which affects the typing of add and put).

Also, cursor types haven't been updated. Creating a cursor over one value (for example 'foo') and expecting the value type to be narrowed (string instead of string | number) seems like an obscure use-case. It didn't seem worth the added complexity but I'll implement it if requested. A more common use case would be iterating over multiple values for a single key in an index but we can't do that.

Resolves #275.