Using TypeScript, how do I strongly type mysql query results

// the function should return a Promise that resolves to 
{ uid: number, uname: string }

// but typeof rows is 
{ rows: { uid: number, uname: string } }

Executing the example, the result I get is something like:

[ TextRow { uid: 1, uname: 'foo', email: '[email protected]' } ]

So pool.query is returning an array with an array of IUser as first element.

Returning multiple users:

class UserController {
    // returns all users
    public async getUsers(): Promise<Array<IUser>> {
        const [rows]: [Array<IUser>] = await pool.query(
                "SELECT * FROM `user`", []);
        return rows; // rows is Array<IUser> so it matches the promise type
    }
}

Returning a specific user:

class UserController {
    public async getUser(id: number): Promise<IUser> { // Note the 
        const [rows]: [Array<IUser>] = 
            await pool.query("SELECT * FROM `user` WHERE `email` = ?",
                            ["[email protected]"]);
        // If email is unique as it would be expected it will 
        // return a single value inside an array
        // rows is still Array<IUser>
        return rows[0]; // rows[0] is an IUser 
    }
}

Using [email protected], [email protected], @types/mysql2

The interface must extend RowDataPacket:

interface IUser extends RowDataPacket {
  ssid: string
}

From there you can pass in the type:

const [rows]: [IUser[], FieldPacket[]] = await connection.query<IUser[]>("SELECT ssid FROM user", [])

or simply

const [rows] = await connection.query<IUser[]>("SELECT ssid FROM user", [])

Using pool.query<type> was not enough for me when trying to set variables that were going to be used later because the datatype RowDataPacket was still infringing on my types...

I ended up creating a wrapper for the pool's query function. Looks like this:

type Row = import("mysql2").RowDataPacket
type Ok = import("mysql2").OkPacket
type dbDefaults = Row[] | Row[][] | Ok[] | Ok
type dbQuery<T> = T & dbDefaults

export const db = {
  query: async <T>(query: string, params?: Array<any>): Promise<[T, any]> => {
    return pool.promise().query<dbQuery<T>>(query, params)
  },
}

This allows me to use the query function in other parts of the project similar to other answers as

let result = await db.query<desiredType>("SELECT", [optionalArgs])

but it strips away the type information for RowDataPacket, etc...

Tags:

Typescript