Skip to content

Commit

Permalink
fix(types): add missing types to TypeCast (#2390)
Browse files Browse the repository at this point in the history
* fix(types): add missing types to TypeCast

* chore: add comment on typeCast for execute

* ci: create strict tests for typeCast option
  • Loading branch information
wellwelwel committed Jan 23, 2024
1 parent 8dd11b2 commit 78ce495
Show file tree
Hide file tree
Showing 4 changed files with 281 additions and 28 deletions.
154 changes: 154 additions & 0 deletions test/tsc-build/strict-checks/typeCast.ts
@@ -0,0 +1,154 @@
import { QueryOptions, ConnectionOptions } from '../../../index.js';
import {
QueryOptions as QueryOptionsP,
ConnectionOptions as ConnectionOptionsP,
} from '../../../promise.js';
import { access, sql } from '../promise/baseConnection.js';

// Callback: QueryOptions
{
const options1: QueryOptions = {
sql,
typeCast: true,
};

const options2: QueryOptions = {
sql,
typeCast: false,
};

const options3: QueryOptions = {
sql,
typeCast: (field, next) => {
const db: string = field.db;
const length: number = field.length;
const name: string = field.name;
const table: string = field.table;
const type: string = field.type;
const buffer: Buffer | null = field.buffer();
const string: string | null = field.string();
const geometry:
| { x: number; y: number }
| { x: number; y: number }[]
| null = field.geometry();

console.log(db, length, name, table, type);
console.log(buffer, string, geometry);

return next();
},
};

console.log(options1, options2, options3);
}

// Callback: ConnectionOptions
{
const options1: ConnectionOptions = {
...access,
typeCast: true,
};

const options2: ConnectionOptions = {
...access,
typeCast: false,
};

const options3: ConnectionOptions = {
...access,
typeCast: (field, next) => {
const db: string = field.db;
const length: number = field.length;
const name: string = field.name;
const table: string = field.table;
const type: string = field.type;
const buffer: Buffer | null = field.buffer();
const string: string | null = field.string();
const geometry:
| { x: number; y: number }
| { x: number; y: number }[]
| null = field.geometry();

console.log(db, length, name, table, type);
console.log(buffer, string, geometry);

return next();
},
};

console.log(options1, options2, options3);
}

// Promise: QueryOptions
{
const options1: QueryOptionsP = {
sql,
typeCast: true,
};

const options2: QueryOptionsP = {
sql,
typeCast: false,
};

const options3: QueryOptionsP = {
sql,
typeCast: (field, next) => {
const db: string = field.db;
const length: number = field.length;
const name: string = field.name;
const table: string = field.table;
const type: string = field.type;
const buffer: Buffer | null = field.buffer();
const string: string | null = field.string();
const geometry:
| { x: number; y: number }
| { x: number; y: number }[]
| null = field.geometry();

console.log(db, length, name, table, type);
console.log(buffer, string, geometry);

return next();
},
};

console.log(options1, options2, options3);
}

// Promise: ConnectionOptions
{
const options1: ConnectionOptionsP = {
...access,
typeCast: true,
};

const options2: ConnectionOptionsP = {
...access,
typeCast: false,
};

const options3: ConnectionOptionsP = {
...access,
typeCast: (field, next) => {
const db: string = field.db;
const length: number = field.length;
const name: string = field.name;
const table: string = field.table;
const type: string = field.type;
const buffer: Buffer | null = field.buffer();
const string: string | null = field.string();
const geometry:
| { x: number; y: number }
| { x: number; y: number }[]
| null = field.geometry();

console.log(db, length, name, table, type);
console.log(buffer, string, geometry);

return next();
},
};

console.log(options1, options2, options3);
}
49 changes: 36 additions & 13 deletions typings/mysql/lib/Connection.d.ts
Expand Up @@ -19,6 +19,7 @@ import { Connection as PromiseConnection } from '../../../promise.js';
import { AuthPlugin } from './Auth.js';
import { QueryableBase } from './protocol/sequences/QueryableBase.js';
import { ExecutableBase } from './protocol/sequences/ExecutableBase.js';
import { TypeCast } from './parsers/typeCast.js';

export interface SslOptions {
/**
Expand Down Expand Up @@ -172,26 +173,48 @@ export interface ConnectionOptions {
infileStreamFactory?: (path: string) => Readable;

/**
* Determines if column values should be converted to native JavaScript types. It is not recommended (and may go away / change in the future)
* to disable type casting, but you can currently do so on either the connection or query level. (Default: true)
* Determines if column values should be converted to native JavaScript types.
*
* You can also specify a function (field: any, next: () => void) => {} to do the type casting yourself.
* @default true
*
* WARNING: YOU MUST INVOKE the parser using one of these three field functions in your custom typeCast callback. They can only be called once.
* It is not recommended (and may go away / change in the future) to disable type casting, but you can currently do so on either the connection or query level.
*
* field.string()
* field.buffer()
* field.geometry()
* ---
*
* are aliases for
* You can also specify a function to do the type casting yourself:
* ```ts
* (field: Field, next: () => void) => {
* return next();
* }
* ```
*
* parser.parseLengthCodedString()
* parser.parseLengthCodedBuffer()
* parser.parseGeometryValue()
* ---
*
* You can find which field function you need to use by looking at: RowDataPacket.prototype._typeCast
* **WARNING:**
*
* YOU MUST INVOKE the parser using one of these three field functions in your custom typeCast callback. They can only be called once:
*
* ```js
* field.string();
* field.buffer();
* field.geometry();
* ```
* Which are aliases for:
*
* ```js
* parser.parseLengthCodedString();
* parser.parseLengthCodedBuffer();
* parser.parseGeometryValue();
* ```
*
* You can find which field function you need to use by looking at `RowDataPacket.prototype._typeCast`.
*
* ---
*
* For `execute`, please see: [typeCast not supported with .execute #649](https://github.com/sidorares/node-mysql2/issues/649).
*/
typeCast?: boolean | ((field: any, next: () => void) => any);
typeCast?: TypeCast;

/**
* A custom query format function
Expand Down
53 changes: 53 additions & 0 deletions typings/mysql/lib/parsers/typeCast.d.ts
@@ -0,0 +1,53 @@
type Geometry = {
x: number;
y: number;
};

type Type = {
type:
| 'DECIMAL'
| 'TINY'
| 'SHORT'
| 'LONG'
| 'FLOAT'
| 'DOUBLE'
| 'NULL'
| 'TIMESTAMP'
| 'TIMESTAMP2'
| 'LONGLONG'
| 'INT24'
| 'DATE'
| 'TIME'
| 'TIME2'
| 'DATETIME'
| 'DATETIME2'
| 'YEAR'
| 'NEWDATE'
| 'VARCHAR'
| 'BIT'
| 'JSON'
| 'NEWDECIMAL'
| 'ENUM'
| 'SET'
| 'TINY_BLOB'
| 'MEDIUM_BLOB'
| 'LONG_BLOB'
| 'BLOB'
| 'VAR_STRING'
| 'STRING'
| 'GEOMETRY';
};

type Field = Type & {
length: number;
db: string;
table: string;
name: string;
string: () => string | null;
buffer: () => Buffer | null;
geometry: () => Geometry | Geometry[] | null;
};

type Next = () => void;

export type TypeCast = ((field: Field, next: Next) => any) | boolean;
53 changes: 38 additions & 15 deletions typings/mysql/lib/protocol/sequences/Query.d.ts
@@ -1,6 +1,7 @@
import { Sequence } from './Sequence.js';
import { OkPacket, RowDataPacket, FieldPacket } from '../packets/index.js';
import { Readable } from 'stream';
import { TypeCast } from '../../parsers/typeCast.js';

export interface QueryOptions {
/**
Expand Down Expand Up @@ -33,26 +34,48 @@ export interface QueryOptions {
nestTables?: any;

/**
* Determines if column values should be converted to native JavaScript types. It is not recommended (and may go away / change in the future)
* to disable type casting, but you can currently do so on either the connection or query level. (Default: true)
* Determines if column values should be converted to native JavaScript types.
*
* You can also specify a function (field: any, next: () => void) => {} to do the type casting yourself.
* @default true
*
* WARNING: YOU MUST INVOKE the parser using one of these three field functions in your custom typeCast callback. They can only be called once.
* It is not recommended (and may go away / change in the future) to disable type casting, but you can currently do so on either the connection or query level.
*
* field.string()
* field.buffer()
* field.geometry()
* ---
*
* are aliases for
* You can also specify a function to do the type casting yourself:
* ```ts
* (field: Field, next: () => void) => {
* return next();
* }
* ```
*
* parser.parseLengthCodedString()
* parser.parseLengthCodedBuffer()
* parser.parseGeometryValue()
* ---
*
* You can find which field function you need to use by looking at: RowDataPacket.prototype._typeCast
* **WARNING:**
*
* YOU MUST INVOKE the parser using one of these three field functions in your custom typeCast callback. They can only be called once:
*
* ```js
* field.string();
* field.buffer();
* field.geometry();
* ```
* Which are aliases for:
*
* ```js
* parser.parseLengthCodedString();
* parser.parseLengthCodedBuffer();
* parser.parseGeometryValue();
* ```
*
* You can find which field function you need to use by looking at `RowDataPacket.prototype._typeCast`.
*
* ---
*
* For `execute`, please see: [typeCast not supported with .execute #649](https://github.com/sidorares/node-mysql2/issues/649).
*/
typeCast?: any;
typeCast?: TypeCast;

/**
* This overrides the same option set at the connection level.
Expand Down Expand Up @@ -137,11 +160,11 @@ declare class Query extends Sequence {
on(event: 'error', listener: (err: QueryError) => any): this;
on(
event: 'fields',
listener: (fields: FieldPacket, index: number) => any
listener: (fields: FieldPacket, index: number) => any,
): this;
on(
event: 'result',
listener: (result: RowDataPacket | OkPacket, index: number) => any
listener: (result: RowDataPacket | OkPacket, index: number) => any,
): this;
on(event: 'end', listener: () => any): this;
}
Expand Down

0 comments on commit 78ce495

Please sign in to comment.