Skip to content

Commit e8140f3

Browse files
committed
chore: lint custom ts/no-arrow-parameter-types
1 parent bd49ad9 commit e8140f3

File tree

9 files changed

+157
-26
lines changed

9 files changed

+157
-26
lines changed

eslint.config.ts

Lines changed: 128 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,16 @@ export default defineConfig(
3131
},
3232
//#endregion
3333

34+
//#region prettier
35+
eslintPluginPrettierRecommended,
36+
//#endregion
37+
3438
//#region eslint (js)
3539
eslint.configs.recommended,
3640
{
3741
name: 'eslint overrides',
3842
rules: {
43+
curly: ['error', 'all'],
3944
eqeqeq: ['error', 'always', { null: 'ignore' }],
4045
'logical-assignment-operators': 'error',
4146
'no-else-return': 'error',
@@ -167,7 +172,7 @@ export default defineConfig(
167172
//#endregion
168173

169174
//#region unicorn
170-
eslintPluginUnicorn.configs['flat/recommended'],
175+
eslintPluginUnicorn.configs.recommended,
171176
{
172177
name: 'unicorn overrides',
173178
rules: {
@@ -199,10 +204,6 @@ export default defineConfig(
199204
},
200205
//#endregion
201206

202-
//#region prettier
203-
eslintPluginPrettierRecommended,
204-
//#endregion
205-
206207
//#region overrides
207208
{
208209
name: 'test/**/*.ts overrides',
@@ -246,6 +247,127 @@ export default defineConfig(
246247
'@typescript-eslint/no-throw-literal': 'off',
247248
'unicorn/no-array-reduce': 'off',
248249
},
249-
}
250+
},
250251
//#endregion
252+
253+
{
254+
name: 'custom',
255+
plugins: {
256+
custom: {
257+
rules: {
258+
'no-arrow-parameter-types': {
259+
meta: {
260+
fixable: 'code',
261+
hasSuggestions: true,
262+
type: 'suggestion',
263+
dialects: ['typescript'],
264+
schema: [
265+
{
266+
type: 'object',
267+
properties: {
268+
allowOptional: {
269+
type: 'boolean',
270+
default: false,
271+
description:
272+
'Allow type annotations when the parameter is optional. Sometimes useful for overloaded functions.',
273+
},
274+
},
275+
},
276+
],
277+
defaultOptions: [
278+
{
279+
allowOptional: false,
280+
},
281+
],
282+
},
283+
create(context) {
284+
const options = context.options[0] as { allowOptional: boolean };
285+
286+
return {
287+
ArrowFunctionExpression(node) {
288+
const paramsWithTypeAnnotation = node.params.filter(
289+
(
290+
// @ts-expect-error: will be inferred when moved into an official plugin
291+
param
292+
) => param.typeAnnotation !== undefined
293+
);
294+
295+
const isCatchClause =
296+
node.parent.callee?.property?.name === 'catch';
297+
298+
if (paramsWithTypeAnnotation.length > 0 && !isCatchClause) {
299+
for (const param of paramsWithTypeAnnotation) {
300+
if (param.optional && options.allowOptional) {
301+
continue;
302+
}
303+
304+
context.report({
305+
node: param,
306+
message:
307+
'Arrow function parameters should not have type annotations. Instead the Object where the operation is used should be typed correctly.',
308+
fix(fixer) {
309+
if (param.optional) {
310+
return null;
311+
}
312+
313+
// TODO @Shinigami92 2025-06-16: Handle async arrow functions
314+
if (node.parent.type === 'VariableDeclarator') {
315+
const variableDeclaratorNode = node.parent;
316+
317+
return [
318+
// Remove ` =>`
319+
fixer.replaceTextRange(
320+
[node.body.range[0] - 3, node.body.range[0]],
321+
''
322+
),
323+
// Remove ` = `
324+
fixer.replaceTextRange(
325+
[
326+
variableDeclaratorNode.id.range[1],
327+
variableDeclaratorNode.init.range[0],
328+
],
329+
''
330+
),
331+
// Replace `const ` with `function `
332+
fixer.replaceTextRange(
333+
[
334+
variableDeclaratorNode.parent.range[0],
335+
variableDeclaratorNode.range[0],
336+
],
337+
'function '
338+
),
339+
];
340+
}
341+
342+
return fixer.removeRange(param.typeAnnotation.range);
343+
},
344+
suggest: [
345+
{
346+
desc: 'Remove type annotation',
347+
fix(fixer) {
348+
if (param.optional) {
349+
return fixer.removeRange([
350+
param.typeAnnotation.range[0] - 1, // Remove the `?` before the type annotation
351+
param.typeAnnotation.range[1],
352+
]);
353+
}
354+
355+
return null;
356+
},
357+
},
358+
],
359+
});
360+
}
361+
}
362+
},
363+
};
364+
},
365+
},
366+
},
367+
},
368+
},
369+
rules: {
370+
'custom/no-arrow-parameter-types': ['error', { allowOptional: true }],
371+
},
372+
}
251373
);

src/db.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export function db(
106106
});
107107

108108
const query: DBConnection['query'] = async (
109-
queryTextOrConfig: string | QueryConfig | QueryArrayConfig,
109+
queryTextOrConfig,
110110
values?: any[]
111111
): Promise<QueryArrayResult | QueryResult> => {
112112
await createConnection();
@@ -147,20 +147,23 @@ ${error}
147147
};
148148

149149
const select: DBConnection['select'] = async (
150-
queryTextOrConfig: string | QueryConfig | QueryArrayConfig,
150+
queryTextOrConfig,
151151
values?: any[]
152152
) => {
153153
const { rows } = await query(queryTextOrConfig, values);
154154
return rows;
155155
};
156156

157157
const column: DBConnection['column'] = async (
158-
columnName: string,
159-
queryTextOrConfig: string | QueryConfig | QueryArrayConfig,
158+
columnName,
159+
queryTextOrConfig,
160160
values?: any[]
161161
) => {
162-
const rows = await select(queryTextOrConfig, values);
163-
return rows.map((r: { [key: string]: any }) => r[columnName]);
162+
const rows: Array<{ [key: string]: any }> = await select(
163+
queryTextOrConfig,
164+
values
165+
);
166+
return rows.map((r) => r[columnName]);
164167
};
165168

166169
return {

src/migrationBuilder.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -825,9 +825,11 @@ export class MigrationBuilder {
825825
// This function wraps each operation within a function that either calls
826826
// the operation or its reverse, and appends the result
827827
// (array of sql statements) to the steps array
828-
const wrap =
829-
<TOperation extends Operation>(operation: TOperation) =>
830-
(...args: Parameters<TOperation>) => {
828+
const wrap: <TOperation extends Operation>(
829+
operation: TOperation
830+
) => (...args: Parameters<TOperation>) => void =
831+
(operation) =>
832+
(...args) => {
831833
if (this._REVERSE_MODE) {
832834
if (typeof operation.reverse !== 'function') {
833835
const name = `pgm.${operation.name}()`;
@@ -970,9 +972,11 @@ export class MigrationBuilder {
970972

971973
// Expose DB so we can access database within transaction
972974
/* eslint-disable @typescript-eslint/no-explicit-any */
973-
const wrapDB =
974-
<T extends any[], TResult>(operation: (...args: T) => TResult) =>
975-
(...args: T) => {
975+
const wrapDB: <T extends any[], TResult>(
976+
operation: (...args: T) => TResult
977+
) => (...args: T) => TResult =
978+
(operation) =>
979+
(...args) => {
976980
if (this._REVERSE_MODE) {
977981
throw new Error('Impossible to automatically infer down migration');
978982
}

src/operations/operators/addToOperatorFamily.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ export type AddToOperatorFamilyFn = (
1212

1313
export type AddToOperatorFamily = Reversible<AddToOperatorFamilyFn>;
1414

15-
export const addToOperatorFamily = (
15+
export const addToOperatorFamily: (
1616
mOptions: MigrationOptions
17-
): AddToOperatorFamily => {
17+
) => AddToOperatorFamily = (mOptions) => {
1818
const method: AddToOperatorFamily = (
1919
operatorFamilyName,
2020
indexMethod,

src/operations/operators/removeFromOperatorFamily.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ export type RemoveFromOperatorFamily = (
99
operatorList: OperatorListDefinition[]
1010
) => string;
1111

12-
export const removeFromOperatorFamily = (
12+
export const removeFromOperatorFamily: (
1313
mOptions: MigrationOptions
14-
): RemoveFromOperatorFamily => {
14+
) => RemoveFromOperatorFamily = (mOptions) => {
1515
const method: RemoveFromOperatorFamily = (
1616
operatorFamilyName,
1717
indexMethod,

src/operations/schemas/createSchema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export type CreateSchemaFn = (
1515
export type CreateSchema = Reversible<CreateSchemaFn>;
1616

1717
export function createSchema(mOptions: MigrationOptions): CreateSchema {
18-
const _create: CreateSchema = (schemaName: string, options = {}) => {
18+
const _create: CreateSchema = (schemaName, options = {}) => {
1919
const { ifNotExists = false, authorization } = options;
2020

2121
const ifNotExistsStr = ifNotExists ? ' IF NOT EXISTS' : '';

src/operations/tables/shared.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -439,10 +439,10 @@ export function parseLike(
439439
like: Name | { table: Name; options?: LikeOptions },
440440
literal: Literal
441441
): string {
442-
const formatOptions = (
442+
const formatOptions: (
443443
name: 'INCLUDING' | 'EXCLUDING',
444444
options?: Like | Like[]
445-
) =>
445+
) => string = (name, options) =>
446446
toArray(options)
447447
.filter((option): option is Like => option !== undefined)
448448
.map((option) => ` ${name} ${option}`)

src/operations/triggers/createTrigger.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ export type CreateTrigger = Reversible<CreateTriggerFn>;
2626

2727
export function createTrigger(mOptions: MigrationOptions): CreateTrigger {
2828
const _create: CreateTrigger = (
29+
/* eslint-disable custom/no-arrow-parameter-types */
2930
tableName: Name,
3031
triggerName: string,
3132
triggerOptions:
3233
| (TriggerOptions & DropOptions)
3334
| (TriggerOptions & FunctionOptions & DropOptions),
3435
definition?: Value
36+
/* eslint-enable custom/no-arrow-parameter-types */
3537
) => {
3638
const {
3739
constraint = false,

test/ts/customRunner.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type Options =
1313
| ({ databaseUrl: string } & TestOptions)
1414
| ({ dbClient: Client } & TestOptions);
1515

16-
export const run = async (options: Options): Promise<boolean> => {
16+
export const run: (options: Options) => Promise<boolean> = async (options) => {
1717
const opts: Omit<RunnerOption, 'direction'> & Options = {
1818
migrationsTable: 'migrations',
1919
dir: resolve(import.meta.dirname, 'migrations'),

0 commit comments

Comments
 (0)