-
Notifications
You must be signed in to change notification settings - Fork 33
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
Generic ResultSet
for better typing
#218
Comments
i second this! |
@abdelkd can you elaborate a little more on the behavior of ResultSet you desire? |
Yes. NeedWhen writing rows to a table, we adhere to a schema for the columns. When querying that table, we know the schema of the columns that will be returned as ProblemThe typing for the // @libsql/core/lib-esm/api.d.ts
export interface ResultSet {
rows: Array<Row>;
...
export interface Row {
length: number;
[index: number]: Value;
[name: string]: Value;
} The keys/values should be the actual column names and types returned from the query. Instead, we get errors. type File = { path: string, content: string, meta: { createdBy: User } }
const { rows } = await db.execute(`SELECT * FROM files`);
const file = rows[0];
file; // Hardcoded to Row, should be File
file.meta; // Hardcoded to Value => string|number|bigint|ArrayBuffer|null
file.meta.createdBy; // ERROR - Value has no createdBy HackThis is a counter example, please fix! 🙂 const file = rows[0] as unknown as File; // cast Value to unknown to recast to File 😞 SolutionTurso doesn't know what its consumer's column names and values will be. Instead of hardcoding types essentially to Here is how // @libsql/core/lib-esm/api.d.ts
export interface Client {
execute<TRow extends Row>(stmt: InStatement): Promise<ResultSet<TRow>>;
...
export interface ResultSet<TRow extends Row> {
columns: Array<keyof TRow>;
columnTypes: Array<string>;
rows: Array<TRow>;
} Now, we can pass a generic argument, where we can inform the query of what the rows are: type File = { path: string, content: string, meta: { createdBy: User } }
const { rows } = await db.execute<File>(`SELECT * FROM files`);
const file = rows[0];
file; // File
file.meta; // File['meta'] => { createdBy: User }
file.meta.createdBy; // File['meta']['user'] 🤗 This would need to be done for all similar methods and return types. So that all return types can be typed by the user. Caveat - I didn't compile/validate the details here, but it should be enough to get the idea. |
The
db.execute
method currently returns a non-genericResultSet
type. However, in some cases, the column and row types are known beforehand. By makingResultSet
generic, developers could provide these types, enhancing the developer experience (DX).For instance, a table with columns
imageId
,filename
, anddata
only provides alength
property when using the standarddb.execute
return type. To return thedata
column in aResponse
object (new Response(result.data)
), a workaround like this is needed:This allows functions to return an
ExtendedResultSet<ImageRow>
, where therows
property is typed based on the specific row type.This approach saves time and reduces debugging when table modifications occur. I'm Happy to contribute to this improvement if the team approves.
The text was updated successfully, but these errors were encountered: