如何使用TypeScript对mysql查询结果进行强类型化

k0pti3hp  于 2023-02-14  发布在  TypeScript
关注(0)|答案(6)|浏览(182)

我是Typescript的新手,不知道我是否正确地强输入了查询结果。下面是我的代码的精髓...

import mysql2, {Pool} from "mysql2";
const pool: Pool = mysql2.createPool({...}).promise();

interface IUser {
    uid   : number;
    uname : string;
}

class UserController {

    public async getUser(id: number): Promise<IUser> {
        const [rows]: Array<{rows: IUser}> = await pool.query(
            "SELECT * FROM `users` WHERE `email` = ?",["me@me.org"]);        
        return rows;
    }
}

TypeScript编译器(3.3.1)抱怨我的return rows语句。
TS2740:类型"{行:"IUser ;}"缺少类型"IUser"的以下属性:uid和uname。
如果我忽略// @ts-ignore的返回,一切都很好。我很好地返回了我的对象,没有任何错误。
我做错什么了吗?
我做了一些修改,但我真的很困惑为什么TypeScript没有抱怨。它看起来一点也不正确。

class UserController {

        public async getUser(id: number): Promise<{rows: IUser}> {
            const [rows]: Array<{rows: IUser}> = await pool.query(
                "SELECT * FROM `users` LIMIT 3");        
            return rows;
        }
    }

是这样吗???
所以,不,那都是错的。
当我想到query返回[rows, fields]时,它开始变得更有意义了。我认为@joesph-climber是正确的,只是语法做了一些调整。
这对我来说很有效也很有意义...

class UserController {

        public async getUser(id: number): Promise<Array<{rows: IUser}>> {
            const [rows]: [Array<{rows: IUser}>] = await pool.query(
                "SELECT * FROM `users` LIMIT 3");        
            return rows;
        }
    }

这也是可行的,而且可能更容易理解。

class UserController {

        public async getUser(id: number): Promise<IUser[]> {
            const [rows]: [IUser[]] = await pool.query(
                "SELECT * FROM `users` LIMIT 3");        
            return rows;
        }
    }
yfjy0ee7

yfjy0ee71#

使用mysql2@1.7.0、类型脚本@3.6.3、@类型/mysql2
接口必须扩展RowDataPacket:

interface IUser extends RowDataPacket {
  ssid: string
}

从那里你可以传入类型:

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

或者干脆

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

vi4fp9gy2#

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

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

执行该示例,得到的结果如下所示:

[ TextRow { uid: 1, uname: 'foo', email: 'foo@mail.com' } ]

因此,pool.query 返回一个数组,其中IUser数组作为第一个元素。
返回多个用户:

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
    }
}

返回特定用户:

class UserController {
    public async getUser(id: number): Promise<IUser> { // Note the 
        const [rows]: [Array<IUser>] = 
            await pool.query("SELECT * FROM `user` WHERE `email` = ?",
                            ["foo@mail.com"]);
        // 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 
    }
}
oyxsuwqo

oyxsuwqo3#

当我试图设置稍后要使用的变量时,使用pool.query<type>对我来说是不够的,因为数据类型RowDataPacket仍然侵犯了我的类型...
我最终为poolquery函数创建了一个 Package 器,如下所示:

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)
  },
}

这允许我在项目的其他部分使用query函数,类似于其他答案,如

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

但是它去掉了RowDataPacket的类型信息等等。

wnrlj8wa

wnrlj8wa4#

我有同样的错误,所以我在谷歌搜索,发现这个例子:https://github.com/types/mysql2/blob/master/test/promise.ts
我代码是这样的

const [user] = await pool.query<RowDataPacket[]>("SELECT * FROM Users WHERE email = ?", [email]);
        if(!user.length) {
            throw {
                error: true,
                message: 'no user founded'
            } 
        }

有了这个,我就可以使用vs代码的自动完成功能,而且不会出现错误

epggiuax

epggiuax5#

我觉得这个很好用

import {RowDataPacket} from 'mysql2'

      const [rows]= await pool.query<IUser[] & RowDataPacket[][]>(
          "SELECT * FROM `users` LIMIT 3");
icnyk63a

icnyk63a6#

接受答案中的方法对我不起作用(typescript@3.5.2mysql2@1.6.5)。

interface IUserRow {
  ssid: string
}

const [rows]: [IUserRow[]] = await pool.query(
  'select ssid from user where id = ?',
  [session.userId],
)

给了我

error TS2322: Type '[RowDataPacket[] | RowDataPacket[][] | OkPacket | OkPacket[], FieldPacket[]]' is not assignable to type '[IUserRow[]]'.
  Types of property '0' are incompatible.
    Type 'RowDataPacket[] | RowDataPacket[][] | OkPacket | OkPacket[]' is not assignable to type 'IUserRow[]'.
      Type 'RowDataPacket[]' is not assignable to type 'IUserRow[]'.
        Property 'ssid' is missing in type 'RowDataPacket' but required in type 'IUserRow'.

突出显示[rows]
我能够让一切工作与:

interface IUserRow {
  ssid: string
}

const [rows] = await pool.query(
  'select ssid from user where id = ?',
  [session.userId],
)

// This first if statement is a type guard against rows possibly being OkPacket
if (Array.isArray(rows) && rows.length > 0) {
  // The casting works fine here
  const userRow = rows[0] as IUserRow
  if (userRow.ssid !== session.ssid) {
    ...
  }
}

我不是打字Maven,所以可能有更好的方法来做到这一点!尽管如此,我希望我可以潜在地节省一些人的挫折,如果他们也有问题的接受答案。

相关问题