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

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> {
instance: M,
options: InstanceUpdateOptions<TAttributes> | CreateOptions<TAttributes>
): HookReturn;
beforeBulkCreate(instances: readonly M[], options: BulkCreateOptions<TAttributes>): HookReturn;
beforeBulkCreate(instances: M[], options: BulkCreateOptions<TAttributes>): HookReturn;
afterBulkCreate(instances: readonly M[], options: BulkCreateOptions<TAttributes>): HookReturn;
beforeBulkDestroy(options: DestroyOptions<TAttributes>): HookReturn;
afterBulkDestroy(options: DestroyOptions<TAttributes>): HookReturn;
......
......@@ -70,7 +70,7 @@ export interface Paranoid {
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
......@@ -126,7 +126,7 @@ export interface AnyOperator {
[Op.any]: readonly (string | number)[];
}
/** Undocumented? */
/** TODO: Undocumented? */
export interface AllOperator {
[Op.all]: readonly (string | number | Date | Literal)[];
}
......@@ -444,7 +444,7 @@ export interface IncludeOptions extends Filterable<any>, Projectable, Paranoid {
/**
* Load further nested related models
*/
include?: readonly Includeable[];
include?: Includeable[];
/**
* Order include. Only available when setting `separate` to true.
......@@ -464,16 +464,16 @@ export type OrderItem =
| Fn
| Col
| Literal
| readonly [OrderItemColumn, string]
| readonly [OrderItemAssociation, OrderItemColumn]
| readonly [OrderItemAssociation, OrderItemColumn, string]
| readonly [OrderItemAssociation, OrderItemAssociation, OrderItemColumn]
| readonly [OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string]
| readonly [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn]
| readonly [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string]
| readonly [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn]
| readonly [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string]
export type Order = string | Fn | Col | Literal | readonly OrderItem[];
| [OrderItemColumn, string]
| [OrderItemAssociation, OrderItemColumn]
| [OrderItemAssociation, OrderItemColumn, string]
| [OrderItemAssociation, OrderItemAssociation, OrderItemColumn]
| [OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string]
| [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn]
| [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string]
| [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn]
| [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string]
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
......@@ -482,26 +482,26 @@ export type Order = string | Fn | Col | Literal | readonly OrderItem[];
export type ProjectionAlias = readonly [string | Literal | Fn, string];
export type FindAttributeOptions =
| readonly (string | ProjectionAlias)[]
| (string | ProjectionAlias)[]
| {
exclude: readonly string[];
include?: readonly (string | ProjectionAlias)[];
exclude: string[];
include?: (string | ProjectionAlias)[];
}
| {
exclude?: readonly string[];
include: readonly (string | ProjectionAlias)[];
exclude?: string[];
include: (string | ProjectionAlias)[];
};
export interface IndexHint {
type: IndexHints;
values: readonly string[];
values: string[];
}
export interface IndexHintable {
/**
* MySQL only.
*/
indexHints?: readonly IndexHint[];
indexHints?: IndexHint[];
}
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
......@@ -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
* 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
......@@ -592,7 +592,7 @@ export interface CountOptions<TAttributes = any>
/**
* Include options. See `find` for details
*/
include?: Includeable | readonly Includeable[];
include?: Includeable | Includeable[];
/**
* Apply COUNT(DISTINCT(col))
......@@ -645,7 +645,7 @@ export interface BuildOptions {
*
* TODO: See set
*/
include?: Includeable | readonly Includeable[];
include?: Includeable | Includeable[];
}
export interface Silent {
......@@ -664,7 +664,7 @@ export interface CreateOptions<TAttributes = any> extends BuildOptions, Logging,
/**
* If set, only columns matching those in fields will be saved
*/
fields?: readonly (keyof TAttributes)[];
fields?: (keyof TAttributes)[];
/**
* On Duplicate
......@@ -699,7 +699,7 @@ export interface FindOrCreateOptions<TAttributes = any, TCreationAttributes = TA
/**
* 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
*/
......@@ -713,7 +713,7 @@ export interface UpsertOptions<TAttributes = any> extends Logging, Transactionab
/**
* The fields to insert / update. Defaults to all fields
*/
fields?: readonly (keyof TAttributes)[];
fields?: (keyof TAttributes)[];
/**
* Return the affected rows (only for postgres)
......@@ -733,7 +733,7 @@ export interface BulkCreateOptions<TAttributes = any> extends Logging, Transacti
/**
* 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
......@@ -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,
* 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?: Includeable | readonly Includeable[];
include?: Includeable | Includeable[];
/**
* 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
/**
* 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
......@@ -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
* will be validated and saved.
*/
fields?: readonly (keyof TAttributes)[];
fields?: (keyof TAttributes)[];
/**
* If false, validations won't be run.
......@@ -2297,11 +2297,11 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut
public static beforeBulkCreate<M extends Model>(
this: ModelStatic<M>,
name: string,
fn: (instances: readonly M[], options: BulkCreateOptions<M['_attributes']>) => HookReturn
fn: (instances: M[], options: BulkCreateOptions<M['_attributes']>) => HookReturn
): void;
public static beforeBulkCreate<M extends Model>(
this: ModelStatic<M>,
fn: (instances: readonly M[], options: BulkCreateOptions<M['_attributes']>) => HookReturn
fn: (instances: M[], options: BulkCreateOptions<M['_attributes']>) => HookReturn
): void;
/**
......@@ -2812,7 +2812,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut
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;
......
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";
class TestModel extends Model {}
/*
* covers types/lib/hooks.d.ts
*/
const hooks: Partial<ModelHooks> = {
beforeSave(m, options) {
expectTypeOf(m).toEqualTypeOf<TestModel>();
expectTypeOf(options).toMatchTypeOf<SaveOptions>(); // TODO consider `.toEqualTypeOf` instead ?
},
afterSave(m, options) {
expectTypeOf(m).toEqualTypeOf<TestModel>();
expectTypeOf(options).toMatchTypeOf<SaveOptions>(); // TODO consider `.toEqualTypeOf` instead ?
},
afterFind(m, options) {
expectTypeOf(m).toEqualTypeOf<readonly TestModel[] | TestModel | null>();
expectTypeOf(options).toEqualTypeOf<FindOptions>();
}
};
export const sequelize = new Sequelize('uri', { hooks });
TestModel.init({}, { sequelize, hooks });
TestModel.addHook('beforeSave', hooks.beforeSave!);
TestModel.addHook('afterSave', hooks.afterSave!);
TestModel.addHook('afterFind', hooks.afterFind!);
/*
* covers types/lib/model.d.ts
*/
TestModel.beforeSave(hooks.beforeSave!);
TestModel.afterSave(hooks.afterSave!);
TestModel.afterFind(hooks.afterFind!);
/*
* covers types/lib/sequelize.d.ts
*/
Sequelize.beforeSave(hooks.beforeSave!);
Sequelize.afterSave(hooks.afterSave!);
Sequelize.afterFind(hooks.afterFind!);
Sequelize.afterFind('namedAfterFind', hooks.afterFind!);
{
class TestModel extends Model {}
const hooks: Partial<ModelHooks> = {
beforeSave(m, options) {
expectTypeOf(m).toEqualTypeOf<TestModel>();
expectTypeOf(options).toMatchTypeOf<SaveOptions>(); // TODO consider `.toEqualTypeOf` instead ?
},
afterSave(m, options) {
expectTypeOf(m).toEqualTypeOf<TestModel>();
expectTypeOf(options).toMatchTypeOf<SaveOptions>(); // TODO consider `.toEqualTypeOf` instead ?
},
afterFind(m, options) {
expectTypeOf(m).toEqualTypeOf<readonly TestModel[] | TestModel | null>();
expectTypeOf(options).toEqualTypeOf<FindOptions>();
}
};
const sequelize = new Sequelize('uri', { hooks });
TestModel.init({}, { sequelize, hooks });
TestModel.addHook('beforeSave', hooks.beforeSave!);
TestModel.addHook('afterSave', hooks.afterSave!);
TestModel.addHook('afterFind', hooks.afterFind!);
TestModel.beforeSave(hooks.beforeSave!);
TestModel.afterSave(hooks.afterSave!);
TestModel.afterFind(hooks.afterFind!);
Sequelize.beforeSave(hooks.beforeSave!);
Sequelize.afterSave(hooks.afterSave!);
Sequelize.afterFind(hooks.afterFind!);
Sequelize.afterFind('namedAfterFind', hooks.afterFind!);
}
// #12959
{
const hooks: ModelHooks = 0 as any;
hooks.beforeValidate = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
hooks.beforeCreate = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
hooks.beforeDestroy = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
hooks.beforeRestore = (...args) => { expectTypeOf(args).toEqualTypeOf<SemiDeepWritable<typeof args>>() };
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!