blinkdb-js / blinkdb

🗃️ An in-memory JS database optimized for large scale storage on the frontend.
https://blinkdb.io
MIT License
119 stars 10 forks source link

Property does not exist on type 'Where | Or | And' #28

Closed retorquere closed 1 year ago

retorquere commented 1 year ago

When I have this table:

type CitekeyRecord = {
  itemID: number
  libraryID: number
  itemKey: string
  citationKey: string
  pinned: boolean | 0 | 1
}

const keys keys = createTable<CitekeyRecord>(createDB({ clone: true }), 'citationKeys')({
  primary: 'itemID',
  indexes: ['itemKey', 'libraryID', 'citationKey'],
})

const where: Query<CitekeyRecord, 'itemID'> = {
        where: {
          pinned: { in: [0, false] },
          citationKey: { eq: key.citationKey },
          libraryID: { eq: key.libraryID }
        },
      }
if (Preference.keyScope === 'global') delete where.where.libraryID

I get the error:

Property 'libraryID' does not exist on type 'Where | Or | And'. Property 'libraryID' does not exist on type 'Or'.

I get the same error when I do

const where: Query<CitekeyRecord, 'itemID'> = {
        where: {
          pinned: { in: [0, false] },
          citationKey: { eq: key.citationKey },
        },
      }
if (Preference.keyScope === 'global') where.where.libraryID = { eq: key.libraryID }

How can I declare the query variable?

maradotwebp commented 1 year ago

This is related to Typescript, not to BlinkDB - But I can solve your issue anyway :P


When you declare a variable to be of some type, the typescript compiler assumes that your variable is of that type. It cannot infer anything else beyond that point. In the example below, despite the fact that the literal value of x.a is a string, accessing it shows that its type is string|number - because x is typed as a string|number.

const x: Record<string, string|number> = {
  a: "hi",
  b: 2
};
console.log(x.a); // x.a is string|number

This is the same problem you face - When you declare where to be a Query<CitekeyRecord, 'itemID'>, the TS compiler knows that it's an object with a where key, since every Query<...> has that, but it doesn't know you specified a pinned or citationKey in it.

The solution is to use satisfies instead:

const y = {
  a: "hi",
  b: 2
} satisfies Record<string, string|number>;
console.log(y.a); // y.a is string

This tells the TS compiler "y is some literal value which satisfies the constraint of Record<string, string|number>". If you specify c: undefined in this object, the compiler would complain. You can then pass y to any function that expects a value of type Record<string, string|number.


In your case, you can solve your typing problem by typing where like this:

const where = {
  where: {
    pinned: { in: [0, false] },
    citationKey: { eq: key.citationKey },
    libraryID: { eq: key.libraryID }
  },
} satisfies Query<CitekeyRecord, 'itemID'>;

The satisfies operator is available since TS 4.9. More information available here: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-9.html#the-satisfies-operator