jakearchibald / idb

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

invalid parameter type is reported for getAllFromIndex command despite success result #266

Closed pavlexander closed 2 years ago

pavlexander commented 2 years ago

main issue/topic

I am creating a store with index that has a string type. Later, when I need to retrieve all values belonging to this index I get an error that the string type key is not allowed... But the code compiles and I see the correct results listed. So, there is probably something wrong with function parameter types.

main code:

import { openDB, DBSchema, IDBPDatabase } from "idb";

interface IUserRecord {
  suid: string;
  sid?: number;
}

interface MyDB extends DBSchema {
  data: {
    key: number;
    value: IUserRecord;
    indexes: { storeUserId: number };
  };
}

var db = await openDB<MyDB>("app-cache-feed", 1, {
  upgrade(db) {
    const store = db.createObjectStore("data", {
      keyPath: "sid",
      autoIncrement: true,
    });

    store.createIndex("storeUserId", "suid");
  },
});

then we need to get the indexed data:

  async getAll(suid: string): Promise<IUserRecord[]> {
    return await db.getAllFromIndex("data", "storeUserId", "1");
  }

here I get the error:

Argument of type '"1"' is not assignable to parameter of type 'number | IDBKeyRange | null | undefined'.ts(2345)

As I mentioned - correct data is returned for the ID. So if I type "1" I get records with suid = '1' and if I type suid = '2' so this seems to be purely typescript type/interface error.

I have not checked but probably other functions are affected.

"idb": "^7.0.1",

side question

I also have a bonus question that maybe someone is able to answer. Is it possible to define 2 different interfaces - 1 for data insert and 1 for data retrieval? The ID (in my example sid ) is auto-generated property. So when I add the record - it is always NOT expected to be set (is optional). And when you retrieve the data - the property is always set.

Currently I use the same interface and this causes issues, because the code always assumes that the retrieved data for property sid could be null..

I know that I can probably use mapper to map the results to a new interface when data is retrieved, but maybe there is more elegant solution.. (p.s. typescript user day 1)

edit: actually, after giving it some thought I have done the following:

interface IConcreteUserRecord {
  suid: string;
  sid: number;
}

  async getAll(suid: string): Promise<IConcreteUserRecord[]> {
    return (await db.getAllFromIndex("data", "storeUserId", "1")) as IConcreteUserRecord[];
  }

it works, not sure if this is how it's supposed to be handled though..

pavlexander commented 2 years ago

my bad. The schema had an error

indexes: { storeUserId: string }