不要怂,就是干,撸起袖子干!

Commit e5b8929d by Pedro Augusto de Paula Barbosa Committed by GitHub

fix(types): allow changing values on before hooks (#12970)

1 parent c77b1f3a
...@@ -40,7 +40,7 @@ export interface ModelHooks<M extends Model = Model, TAttributes = any> { ...@@ -40,7 +40,7 @@ export interface ModelHooks<M extends Model = Model, TAttributes = any> {
instance: M, instance: M,
options: InstanceUpdateOptions<TAttributes> | CreateOptions<TAttributes> options: InstanceUpdateOptions<TAttributes> | CreateOptions<TAttributes>
): HookReturn; ): HookReturn;
beforeBulkCreate(instances: readonly M[], options: BulkCreateOptions<TAttributes>): HookReturn; beforeBulkCreate(instances: M[], options: BulkCreateOptions<TAttributes>): HookReturn;
afterBulkCreate(instances: readonly M[], options: BulkCreateOptions<TAttributes>): HookReturn; afterBulkCreate(instances: readonly M[], options: BulkCreateOptions<TAttributes>): HookReturn;
beforeBulkDestroy(options: DestroyOptions<TAttributes>): HookReturn; beforeBulkDestroy(options: DestroyOptions<TAttributes>): HookReturn;
afterBulkDestroy(options: DestroyOptions<TAttributes>): HookReturn; afterBulkDestroy(options: DestroyOptions<TAttributes>): HookReturn;
......
...@@ -70,7 +70,7 @@ export interface Paranoid { ...@@ -70,7 +70,7 @@ export interface Paranoid {
paranoid?: boolean; paranoid?: boolean;
} }
export type GroupOption = string | Fn | Col | readonly (string | Fn | Col)[]; export type GroupOption = string | Fn | Col | (string | Fn | Col)[];
/** /**
* Options to pass to Model on drop * Options to pass to Model on drop
...@@ -126,7 +126,7 @@ export interface AnyOperator { ...@@ -126,7 +126,7 @@ export interface AnyOperator {
[Op.any]: readonly (string | number)[]; [Op.any]: readonly (string | number)[];
} }
/** Undocumented? */ /** TODO: Undocumented? */
export interface AllOperator { export interface AllOperator {
[Op.all]: readonly (string | number | Date | Literal)[]; [Op.all]: readonly (string | number | Date | Literal)[];
} }
...@@ -444,7 +444,7 @@ export interface IncludeOptions extends Filterable<any>, Projectable, Paranoid { ...@@ -444,7 +444,7 @@ export interface IncludeOptions extends Filterable<any>, Projectable, Paranoid {
/** /**
* Load further nested related models * Load further nested related models
*/ */
include?: readonly Includeable[]; include?: Includeable[];
/** /**
* Order include. Only available when setting `separate` to true. * Order include. Only available when setting `separate` to true.
...@@ -464,16 +464,16 @@ export type OrderItem = ...@@ -464,16 +464,16 @@ export type OrderItem =
| Fn | Fn
| Col | Col
| Literal | Literal
| readonly [OrderItemColumn, string] | [OrderItemColumn, string]
| readonly [OrderItemAssociation, OrderItemColumn] | [OrderItemAssociation, OrderItemColumn]
| readonly [OrderItemAssociation, OrderItemColumn, string] | [OrderItemAssociation, OrderItemColumn, string]
| readonly [OrderItemAssociation, OrderItemAssociation, OrderItemColumn] | [OrderItemAssociation, OrderItemAssociation, OrderItemColumn]
| readonly [OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string] | [OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string]
| readonly [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn] | [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn]
| readonly [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string] | [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string]
| readonly [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn] | [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn]
| readonly [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string] | [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string]
export type Order = string | Fn | Col | Literal | readonly OrderItem[]; export type Order = string | Fn | Col | Literal | OrderItem[];
/** /**
* Please note if this is used the aliased property will not be available on the model instance * Please note if this is used the aliased property will not be available on the model instance
...@@ -482,26 +482,26 @@ export type Order = string | Fn | Col | Literal | readonly OrderItem[]; ...@@ -482,26 +482,26 @@ export type Order = string | Fn | Col | Literal | readonly OrderItem[];
export type ProjectionAlias = readonly [string | Literal | Fn, string]; export type ProjectionAlias = readonly [string | Literal | Fn, string];
export type FindAttributeOptions = export type FindAttributeOptions =
| readonly (string | ProjectionAlias)[] | (string | ProjectionAlias)[]
| { | {
exclude: readonly string[]; exclude: string[];
include?: readonly (string | ProjectionAlias)[]; include?: (string | ProjectionAlias)[];
} }
| { | {
exclude?: readonly string[]; exclude?: string[];
include: readonly (string | ProjectionAlias)[]; include: (string | ProjectionAlias)[];
}; };
export interface IndexHint { export interface IndexHint {
type: IndexHints; type: IndexHints;
values: readonly string[]; values: string[];
} }
export interface IndexHintable { export interface IndexHintable {
/** /**
* MySQL only. * MySQL only.
*/ */
indexHints?: readonly IndexHint[]; indexHints?: IndexHint[];
} }
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>> type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
...@@ -521,7 +521,7 @@ export interface FindOptions<TAttributes = any> ...@@ -521,7 +521,7 @@ export interface FindOptions<TAttributes = any>
* If your association are set up with an `as` (eg. `X.hasMany(Y, { as: 'Z }`, you need to specify Z in * If your association are set up with an `as` (eg. `X.hasMany(Y, { as: 'Z }`, you need to specify Z in
* the as attribute when eager loading Y). * the as attribute when eager loading Y).
*/ */
include?: Includeable | readonly Includeable[]; include?: Includeable | Includeable[];
/** /**
* Specifies an ordering. If a string is provided, it will be escaped. Using an array, you can provide * Specifies an ordering. If a string is provided, it will be escaped. Using an array, you can provide
...@@ -592,7 +592,7 @@ export interface CountOptions<TAttributes = any> ...@@ -592,7 +592,7 @@ export interface CountOptions<TAttributes = any>
/** /**
* Include options. See `find` for details * Include options. See `find` for details
*/ */
include?: Includeable | readonly Includeable[]; include?: Includeable | Includeable[];
/** /**
* Apply COUNT(DISTINCT(col)) * Apply COUNT(DISTINCT(col))
...@@ -645,7 +645,7 @@ export interface BuildOptions { ...@@ -645,7 +645,7 @@ export interface BuildOptions {
* *
* TODO: See set * TODO: See set
*/ */
include?: Includeable | readonly Includeable[]; include?: Includeable | Includeable[];
} }
export interface Silent { export interface Silent {
...@@ -664,7 +664,7 @@ export interface CreateOptions<TAttributes = any> extends BuildOptions, Logging, ...@@ -664,7 +664,7 @@ export interface CreateOptions<TAttributes = any> extends BuildOptions, Logging,
/** /**
* If set, only columns matching those in fields will be saved * If set, only columns matching those in fields will be saved
*/ */
fields?: readonly (keyof TAttributes)[]; fields?: (keyof TAttributes)[];
/** /**
* On Duplicate * On Duplicate
...@@ -699,7 +699,7 @@ export interface FindOrCreateOptions<TAttributes = any, TCreationAttributes = TA ...@@ -699,7 +699,7 @@ export interface FindOrCreateOptions<TAttributes = any, TCreationAttributes = TA
/** /**
* The fields to insert / update. Defaults to all fields * The fields to insert / update. Defaults to all fields
*/ */
fields?: readonly (keyof TAttributes)[]; fields?: (keyof TAttributes)[];
/** /**
* Default values to use if building a new instance * Default values to use if building a new instance
*/ */
...@@ -713,7 +713,7 @@ export interface UpsertOptions<TAttributes = any> extends Logging, Transactionab ...@@ -713,7 +713,7 @@ export interface UpsertOptions<TAttributes = any> extends Logging, Transactionab
/** /**
* The fields to insert / update. Defaults to all fields * The fields to insert / update. Defaults to all fields
*/ */
fields?: readonly (keyof TAttributes)[]; fields?: (keyof TAttributes)[];
/** /**
* Return the affected rows (only for postgres) * Return the affected rows (only for postgres)
...@@ -733,7 +733,7 @@ export interface BulkCreateOptions<TAttributes = any> extends Logging, Transacti ...@@ -733,7 +733,7 @@ export interface BulkCreateOptions<TAttributes = any> extends Logging, Transacti
/** /**
* Fields to insert (defaults to all fields) * Fields to insert (defaults to all fields)
*/ */
fields?: readonly (keyof TAttributes)[]; fields?: (keyof TAttributes)[];
/** /**
* Should each row be subject to validation before it is inserted. The whole insert will fail if one row * Should each row be subject to validation before it is inserted. The whole insert will fail if one row
...@@ -758,17 +758,17 @@ export interface BulkCreateOptions<TAttributes = any> extends Logging, Transacti ...@@ -758,17 +758,17 @@ export interface BulkCreateOptions<TAttributes = any> extends Logging, Transacti
* Fields to update if row key already exists (on duplicate key update)? (only supported by MySQL, * Fields to update if row key already exists (on duplicate key update)? (only supported by MySQL,
* MariaDB, SQLite >= 3.24.0 & Postgres >= 9.5). By default, all fields are updated. * MariaDB, SQLite >= 3.24.0 & Postgres >= 9.5). By default, all fields are updated.
*/ */
updateOnDuplicate?: readonly (keyof TAttributes)[]; updateOnDuplicate?: (keyof TAttributes)[];
/** /**
* Include options. See `find` for details * Include options. See `find` for details
*/ */
include?: Includeable | readonly Includeable[]; include?: Includeable | Includeable[];
/** /**
* Return all columns or only the specified columns for the affected rows (only for postgres) * Return all columns or only the specified columns for the affected rows (only for postgres)
*/ */
returning?: boolean | readonly (keyof TAttributes)[]; returning?: boolean | (keyof TAttributes)[];
} }
/** /**
...@@ -846,7 +846,7 @@ export interface UpdateOptions<TAttributes = any> extends Logging, Transactionab ...@@ -846,7 +846,7 @@ export interface UpdateOptions<TAttributes = any> extends Logging, Transactionab
/** /**
* Fields to update (defaults to all fields) * Fields to update (defaults to all fields)
*/ */
fields?: readonly (keyof TAttributes)[]; fields?: (keyof TAttributes)[];
/** /**
* Should each row be subject to validation before it is inserted. The whole insert will fail if one row * Should each row be subject to validation before it is inserted. The whole insert will fail if one row
...@@ -969,7 +969,7 @@ export interface SaveOptions<TAttributes = any> extends Logging, Transactionable ...@@ -969,7 +969,7 @@ export interface SaveOptions<TAttributes = any> extends Logging, Transactionable
* An optional array of strings, representing database columns. If fields is provided, only those columns * An optional array of strings, representing database columns. If fields is provided, only those columns
* will be validated and saved. * will be validated and saved.
*/ */
fields?: readonly (keyof TAttributes)[]; fields?: (keyof TAttributes)[];
/** /**
* If false, validations won't be run. * If false, validations won't be run.
...@@ -2297,11 +2297,11 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut ...@@ -2297,11 +2297,11 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut
public static beforeBulkCreate<M extends Model>( public static beforeBulkCreate<M extends Model>(
this: ModelStatic<M>, this: ModelStatic<M>,
name: string, name: string,
fn: (instances: readonly M[], options: BulkCreateOptions<M['_attributes']>) => HookReturn fn: (instances: M[], options: BulkCreateOptions<M['_attributes']>) => HookReturn
): void; ): void;
public static beforeBulkCreate<M extends Model>( public static beforeBulkCreate<M extends Model>(
this: ModelStatic<M>, this: ModelStatic<M>,
fn: (instances: readonly M[], options: BulkCreateOptions<M['_attributes']>) => HookReturn fn: (instances: M[], options: BulkCreateOptions<M['_attributes']>) => HookReturn
): void; ): void;
/** /**
...@@ -2812,7 +2812,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut ...@@ -2812,7 +2812,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut
public equals(other: this): boolean; public equals(other: this): boolean;
/** /**
* Check if this is eqaul to one of `others` by calling equals * Check if this is equal to one of `others` by calling equals
*/ */
public equalsOneOf(others: readonly this[]): boolean; public equalsOneOf(others: readonly this[]): boolean;
......
import { expectTypeOf } from "expect-type"; import { expectTypeOf } from "expect-type";
import { Model, SaveOptions, Sequelize, FindOptions } from "sequelize"; import { SemiDeepWritable } from "./type-helpers/deep-writable";
import { Model, SaveOptions, Sequelize, FindOptions, ModelCtor, ModelType, ModelDefined, ModelStatic } from "sequelize";
import { ModelHooks } from "../lib/hooks"; import { ModelHooks } from "../lib/hooks";
class TestModel extends Model {} {
class TestModel extends Model {}
/*
* covers types/lib/hooks.d.ts const hooks: Partial<ModelHooks> = {
*/ beforeSave(m, options) {
expectTypeOf(m).toEqualTypeOf<TestModel>();
const hooks: Partial<ModelHooks> = { expectTypeOf(options).toMatchTypeOf<SaveOptions>(); // TODO consider `.toEqualTypeOf` instead ?
beforeSave(m, options) { },
expectTypeOf(m).toEqualTypeOf<TestModel>(); afterSave(m, options) {
expectTypeOf(options).toMatchTypeOf<SaveOptions>(); // TODO consider `.toEqualTypeOf` instead ? expectTypeOf(m).toEqualTypeOf<TestModel>();
}, expectTypeOf(options).toMatchTypeOf<SaveOptions>(); // TODO consider `.toEqualTypeOf` instead ?
afterSave(m, options) { },
expectTypeOf(m).toEqualTypeOf<TestModel>(); afterFind(m, options) {
expectTypeOf(options).toMatchTypeOf<SaveOptions>(); // TODO consider `.toEqualTypeOf` instead ? expectTypeOf(m).toEqualTypeOf<readonly TestModel[] | TestModel | null>();
}, expectTypeOf(options).toEqualTypeOf<FindOptions>();
afterFind(m, options) { }
expectTypeOf(m).toEqualTypeOf<readonly TestModel[] | TestModel | null>(); };
expectTypeOf(options).toEqualTypeOf<FindOptions>();
} const sequelize = new Sequelize('uri', { hooks });
}; TestModel.init({}, { sequelize, hooks });
export const sequelize = new Sequelize('uri', { hooks }); TestModel.addHook('beforeSave', hooks.beforeSave!);
TestModel.init({}, { sequelize, hooks }); TestModel.addHook('afterSave', hooks.afterSave!);
TestModel.addHook('afterFind', hooks.afterFind!);
TestModel.addHook('beforeSave', hooks.beforeSave!);
TestModel.addHook('afterSave', hooks.afterSave!); TestModel.beforeSave(hooks.beforeSave!);
TestModel.addHook('afterFind', hooks.afterFind!); TestModel.afterSave(hooks.afterSave!);
TestModel.afterFind(hooks.afterFind!);
/*
* covers types/lib/model.d.ts Sequelize.beforeSave(hooks.beforeSave!);
*/ Sequelize.afterSave(hooks.afterSave!);
Sequelize.afterFind(hooks.afterFind!);
TestModel.beforeSave(hooks.beforeSave!); Sequelize.afterFind('namedAfterFind', hooks.afterFind!);
TestModel.afterSave(hooks.afterSave!); }
TestModel.afterFind(hooks.afterFind!);
// #12959
/* {
* covers types/lib/sequelize.d.ts const hooks: ModelHooks = 0 as any;
*/
hooks.beforeValidate = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
Sequelize.beforeSave(hooks.beforeSave!); hooks.beforeCreate = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
Sequelize.afterSave(hooks.afterSave!); hooks.beforeDestroy = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
Sequelize.afterFind(hooks.afterFind!); hooks.beforeRestore = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
Sequelize.afterFind('namedAfterFind', hooks.afterFind!); hooks.beforeUpdate = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
hooks.beforeSave = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
hooks.beforeBulkCreate = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
hooks.beforeBulkDestroy = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
hooks.beforeBulkRestore = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
hooks.beforeBulkUpdate = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
hooks.beforeFind = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
hooks.beforeCount = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
hooks.beforeFindAfterExpandIncludeAll = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
hooks.beforeFindAfterOptions = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
hooks.beforeSync = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
hooks.beforeBulkSync = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
}
/**
* Adapted from krzkaczor/ts-essentials
*
* https://github.com/krzkaczor/ts-essentials/blob/v7.1.0/lib/types.ts#L165
*
* Thank you!
*/
import { Model, Sequelize, ModelCtor, ModelType, ModelDefined, ModelStatic } from "sequelize";
type Builtin = string | number | boolean | bigint | symbol | undefined | null | Function | Date | Error | RegExp;
type SequelizeBasic = Builtin | Sequelize | Model | ModelCtor<Model> | ModelType | ModelDefined<any, any> | ModelStatic<Model>;
// type ToMutableArrayIfNeeded<T> = T extends readonly any[]
// ? { -readonly [K in keyof T]: ToMutableArrayIfNeeded<T[K]> }
// : T;
type NoReadonlyArraysDeep<T> = T extends SequelizeBasic
? T
: T extends readonly any[]
? { -readonly [K in keyof T]: NoReadonlyArraysDeep<T[K]> }
: T extends Record<string, any>
? { [K in keyof T]: NoReadonlyArraysDeep<T[K]> }
: T;
type ShallowWritable<T> = T extends Record<string, any> ? { -readonly [K in keyof T]: T[K] } : T;
export type SemiDeepWritable<T> = ShallowWritable<NoReadonlyArraysDeep<T>>;
export type DeepWritable<T> = T extends SequelizeBasic
? T
: T extends Map<infer K, infer V>
? Map<DeepWritable<K>, DeepWritable<V>>
: T extends ReadonlyMap<infer K, infer V>
? Map<DeepWritable<K>, DeepWritable<V>>
: T extends WeakMap<infer K, infer V>
? WeakMap<DeepWritable<K>, DeepWritable<V>>
: T extends Set<infer U>
? Set<DeepWritable<U>>
: T extends ReadonlySet<infer U>
? Set<DeepWritable<U>>
: T extends WeakSet<infer U>
? WeakSet<DeepWritable<U>>
: T extends Promise<infer U>
? Promise<DeepWritable<U>>
: T extends {}
? { -readonly [K in keyof T]: DeepWritable<T[K]> }
: T;
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!