使用TypeScript,如何强烈键入mysql查询结果

时间:2019-02-07 23:34:38

标签: typescript

我是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:类型'{rows: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;
        }
    }

5 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

我遇到了相同的错误,所以我在Google中搜索并建立了以下示例: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代码的自动完成功能,并且没有错误

答案 2 :(得分:0)

接受的答案中的方法对我不起作用(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) {
    ...
  }
}

我不是Typescript专家,所以可能有更好的方法!尽管如此,我希望如果有人也对接受的答案有疑问,我可以避免他们的沮丧。

答案 3 :(得分:0)

使用mysql2 @ 1.7.0,typescript @ 3.6.3,@ types / 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", [])

答案 4 :(得分:0)

在尝试设置稍后将要使用的变量时,仅使用pool.query<type>对我来说是不够的,因为数据类型RowDataPacket仍在侵犯我的类型...

我最终为pool的{​​{1}}函数创建了一个包装器。看起来像这样:

query

这使我可以在项目的其他部分使用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

但它会剥离RowDataPacket的类型信息,等等...