6

I have code similar to this - very simple just to show the case.

this.getCode = (code: string): Promise<codeObject | false> => {
        return new Promise((resolve, reject) => {
            pool.query('SELECT * FROM ?? WHERE code = ?', [TABLE, code], function (err, result) {
                if (err) {
                    return reject(err);
                }

                if (result.length === 0) {
                    return resolve(false);
                }
            });
        });
    };

Problem is in line if (result.length === 0) {, the error is error TS2339: Property 'length' does not exist on type 'RowDataPacket[] | RowDataPacket[][] | OkPacket | OkPacket[]'.

I can not find any way to redefine OkPacket to at least have length?: number that would be enought for ignore that error (most selects do not get OkPackets anyway and i dont wanna have to check type on each select if what i got is not OkPacket when i know it isnt)...

Seti
  • 2,169
  • 16
  • 26
  • What is `[TABLE]` ? – random Apr 05 '19 at 08:06
  • its `const TABLE: string = table_name;` – Seti Apr 05 '19 at 08:07
  • What is your objection to type checking? Seems like the most straight forward way to deal with this. – etarhan Apr 05 '19 at 09:51
  • 2
    In every function that deals with select (most of them) i would need to find out which result is not OkPacket, when i cannot even get that class to make instanceof check. And i dont want to have milions of lines just checking if constructor name is 'OkPacket' as its quite lame really – Seti Apr 05 '19 at 11:01

5 Answers5

7

I faced a similar issue and was able to fix the warning that TypeScript gives, "Property 'length' does not exist on type 'OkPacket'" by checking to see if the resulting rows from the query was an array. This was a lot easier for me to do than converting the rows to a string with JSON.stringify().

Using the original example above, I'd change the result.length comparison to this:

if (Array.isArray(result) && result.length === 0) {
   return resolve(false);
}
1

I couldn't find a proper way to handle this either, so ended up with this workaround for my similar case:

const [rows,] = await connection.execute("SELECT id FROM users WHERE email = ?", [profile.email]);
if ((rows as any).length === 1) {
   return (rows as any)[0].id;
}
logger.error("Unexpected query response", rows);
throw Error("This code should never be executed");

You can also use user defined type-guards. Like this:

const isRowDataPacket = (rows: RowDataPacket[] | RowDataPacket[][] | OkPacket | OkPacket[] | ResultSetHeader): rows is RowDataPacket[] | RowDataPacket[][] => {
    return (rows as RowDataPacket[] | RowDataPacket[][]).length !== undefined
}

But you would still need to make a typecast further to interpret each row correctly:

const [rows,] = await connection.execute("SELECT id FROM users WHERE email = ?", [profile.email]);
if (isRowDataPacket(rows) && rows.length === 1) {
    return (rows[0] as { id: string }).id;
}
Alexander Reshytko
  • 2,126
  • 1
  • 20
  • 28
0

I hate this error as well. This is my workaround:

const [rows] = await db.query(`mySqlHere`);
let tempResult: any = rows;

console.log(tempResult.length); // OK
0

I also dislike the design choice on this, but i usually use :

const [rows, fields] = await conn.execute(QUERY_STRING) as any
console.log(rows.length)
Romain Bruckert
  • 2,546
  • 31
  • 50
0

For my own use case, the map was not defined in OkPacket, because I needed the following

function (error, results, fields) {

            const data = results.map((item: any) => {
                //... do stuffs..
                return {
                    name: item.name,
                    username: item.username,
                    image: item.image,
                    bio: item.bio,
                    url: item.url,
                }
            })
            resolve([results, fields]);
}

To get rid of the error, I ended up checking if map exists in the results object, and making an early return if not (just the reject() won't fix the error). Modified the code as following

function (error, results, fields) {
            if(!('map' in results)) {
                reject(results);
                return;
            }
            const data = results.map((item: any) => {
                // ...do stuff...
                return {
                    name: item.name,
                    username: item.username,
                    image: item.image,
                    bio: item.bio,
                    url: item.url,
                }
            })
            resolve([results, fields]);
}

For your case you could check for the length, for example, and make an early return if it does not exist.

if(!('length' in results)) {
     reject(results);
     return;
}
Gogol
  • 3,033
  • 4
  • 28
  • 57