Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support discriminated union type or similar in database schemas #275

Open
darrylnoakes opened this issue Jun 22, 2022 · 3 comments · May be fixed by #291
Open

Support discriminated union type or similar in database schemas #275

darrylnoakes opened this issue Jun 22, 2022 · 3 comments · May be fixed by #291

Comments

@darrylnoakes
Copy link

Currently, there is no way to strongly type a keyval store that has only certain keys (each with its own value type).

For example:

interface DatabaseSchema extends DBSchema {
  metadata:
    | {
        key: "foo";
        value: { bar: number };
      }
    | {
        key: "baz";
        value: string;
      };
}

...

const foo = await db.get("metadata", "foo");
// Type of `foo`: string | { bar: number } | undefined

What I would like is some way for a discriminated union (based on key type, obviously) to be detected and used.
The key passed to database functions would then be used to provide better typing.

One of my other ideas for what this could look like, instead of a discriminated union:

interface Metadata extends DBSchemaStore = {
  foo: { bar: number };
  baz: string;
};

interface DatabaseSchema extends DBSchema {
  metadata: Metadata;
}

const foo = await db.get("metadata", "foo");
// Type of `foo`: { bar: number } | undefined
@darrylnoakes
Copy link
Author

Or even something like the following, where the corresponding type for every key is indexable, with the top-level entries of said type being used as the key/value pairs.

Contrived example:

interface DatabaseSchema extends DBSchema {
  posts: Record<string, Post>; // Like `list: { key: string; value: Post }`
  metadata: {
    times: { joined: string, lastSeen: string };
    counts: { posts: number; privateMessages: number }; 
  };
}

@darrylnoakes
Copy link
Author

@indianakernick
Copy link

indianakernick commented Jan 23, 2023

This seems doable. A separate generic parameter would need to be introduced for the key. That key can then be used to narrow the type of the value. Here's an updated version of the StoreValue type and the get function. If the Key parameter on the StoreValue type has a default value of any, then this isn't a breaking change.

export type StoreValue<
  DBTypes extends DBSchema | unknown,
  StoreName extends StoreNames<DBTypes>,
  Key = any
> = DBTypes extends DBSchema
    ? (DBTypes[StoreName] & (Key extends StoreKey<DBTypes, StoreName> ? { key: Key } : {}))['value'] 
    : any;

get<
  Name extends StoreNames<DBTypes>,
  Key extends StoreKey<DBTypes, Name> | IDBKeyRange
>(
  storeName: Name,
  query: Key
): Promise<StoreValue<DBTypes, Name, Key> | undefined>;

This works on the initial example. When anIDBKeyRange is used as the key, the value is the full union of types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants