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

Commit 287607a0 by papb

fix(types): better support for readonly arrays

1 parent 914279aa
...@@ -40,8 +40,8 @@ export interface ModelHooks<M extends Model = Model, TAttributes = any> { ...@@ -40,8 +40,8 @@ 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: M[], options: BulkCreateOptions<TAttributes>): HookReturn; beforeBulkCreate(instances: readonly M[], options: BulkCreateOptions<TAttributes>): HookReturn;
afterBulkCreate(instances: 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;
beforeBulkRestore(options: RestoreOptions<TAttributes>): HookReturn; beforeBulkRestore(options: RestoreOptions<TAttributes>): HookReturn;
...@@ -52,7 +52,7 @@ export interface ModelHooks<M extends Model = Model, TAttributes = any> { ...@@ -52,7 +52,7 @@ export interface ModelHooks<M extends Model = Model, TAttributes = any> {
beforeCount(options: CountOptions<TAttributes>): HookReturn; beforeCount(options: CountOptions<TAttributes>): HookReturn;
beforeFindAfterExpandIncludeAll(options: FindOptions<TAttributes>): HookReturn; beforeFindAfterExpandIncludeAll(options: FindOptions<TAttributes>): HookReturn;
beforeFindAfterOptions(options: FindOptions<TAttributes>): HookReturn; beforeFindAfterOptions(options: FindOptions<TAttributes>): HookReturn;
afterFind(instancesOrInstance: M[] | M | null, options: FindOptions<TAttributes>): HookReturn; afterFind(instancesOrInstance: readonly M[] | M | null, options: FindOptions<TAttributes>): HookReturn;
beforeSync(options: SyncOptions): HookReturn; beforeSync(options: SyncOptions): HookReturn;
afterSync(options: SyncOptions): HookReturn; afterSync(options: SyncOptions): HookReturn;
beforeBulkSync(options: SyncOptions): HookReturn; beforeBulkSync(options: SyncOptions): 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 | (string | Fn | Col)[]; export type GroupOption = string | Fn | Col | readonly (string | Fn | Col)[];
/** /**
* Options to pass to Model on drop * Options to pass to Model on drop
...@@ -103,7 +103,7 @@ export interface ScopeOptions { ...@@ -103,7 +103,7 @@ export interface ScopeOptions {
* any arguments, or an array, where the first element is the name of the method, and consecutive elements * any arguments, or an array, where the first element is the name of the method, and consecutive elements
* are arguments to that method. Pass null to remove all scopes, including the default. * are arguments to that method. Pass null to remove all scopes, including the default.
*/ */
method: string | [string, ...unknown[]]; method: string | readonly [string, ...unknown[]];
} }
/** /**
...@@ -123,15 +123,15 @@ export type WhereOptions<TAttributes = any> = ...@@ -123,15 +123,15 @@ export type WhereOptions<TAttributes = any> =
* _PG only_ * _PG only_
*/ */
export interface AnyOperator { export interface AnyOperator {
[Op.any]: (string | number)[]; [Op.any]: readonly (string | number)[];
} }
/** Undocumented? */ /** Undocumented? */
export interface AllOperator { export interface AllOperator {
[Op.all]: (string | number | Date | Literal)[]; [Op.all]: readonly (string | number | Date | Literal)[];
} }
export type Rangable = [number, number] | [Date, Date] | Literal; export type Rangable = readonly [number, number] | readonly [Date, Date] | Literal;
/** /**
* Operators that can be used in WhereOptions * Operators that can be used in WhereOptions
...@@ -144,7 +144,7 @@ export interface WhereOperators { ...@@ -144,7 +144,7 @@ export interface WhereOperators {
* *
* _PG only_ * _PG only_
*/ */
[Op.any]?: (string | number | Literal)[] | Literal; [Op.any]?: readonly (string | number | Literal)[] | Literal;
/** Example: `[Op.gte]: 6,` becomes `>= 6` */ /** Example: `[Op.gte]: 6,` becomes `>= 6` */
[Op.gte]?: number | string | Date | Literal; [Op.gte]?: number | string | Date | Literal;
...@@ -165,10 +165,10 @@ export interface WhereOperators { ...@@ -165,10 +165,10 @@ export interface WhereOperators {
[Op.between]?: Rangable; [Op.between]?: Rangable;
/** Example: `[Op.in]: [1, 2],` becomes `IN [1, 2]` */ /** Example: `[Op.in]: [1, 2],` becomes `IN [1, 2]` */
[Op.in]?: (string | number | Literal)[] | Literal; [Op.in]?: readonly (string | number | Literal)[] | Literal;
/** Example: `[Op.notIn]: [1, 2],` becomes `NOT IN [1, 2]` */ /** Example: `[Op.notIn]: [1, 2],` becomes `NOT IN [1, 2]` */
[Op.notIn]?: (string | number | Literal)[] | Literal; [Op.notIn]?: readonly (string | number | Literal)[] | Literal;
/** /**
* Examples: * Examples:
...@@ -205,14 +205,14 @@ export interface WhereOperators { ...@@ -205,14 +205,14 @@ export interface WhereOperators {
* *
* Example: `[Op.contains]: [1, 2]` becomes `@> [1, 2]` * Example: `[Op.contains]: [1, 2]` becomes `@> [1, 2]`
*/ */
[Op.contains]?: (string | number)[] | Rangable; [Op.contains]?: readonly (string | number)[] | Rangable;
/** /**
* PG array contained by operator * PG array contained by operator
* *
* Example: `[Op.contained]: [1, 2]` becomes `<@ [1, 2]` * Example: `[Op.contained]: [1, 2]` becomes `<@ [1, 2]`
*/ */
[Op.contained]?: (string | number)[] | Rangable; [Op.contained]?: readonly (string | number)[] | Rangable;
/** Example: `[Op.gt]: 6,` becomes `> 6` */ /** Example: `[Op.gt]: 6,` becomes `> 6` */
[Op.gt]?: number | string | Date | Literal; [Op.gt]?: number | string | Date | Literal;
...@@ -311,12 +311,12 @@ export interface WhereOperators { ...@@ -311,12 +311,12 @@ export interface WhereOperators {
/** Example: `[Op.or]: [{a: 5}, {a: 6}]` becomes `(a = 5 OR a = 6)` */ /** Example: `[Op.or]: [{a: 5}, {a: 6}]` becomes `(a = 5 OR a = 6)` */
export interface OrOperator<TAttributes = any> { export interface OrOperator<TAttributes = any> {
[Op.or]: WhereOptions<TAttributes> | WhereOptions<TAttributes>[] | WhereValue<TAttributes> | WhereValue<TAttributes>[]; [Op.or]: WhereOptions<TAttributes> | readonly WhereOptions<TAttributes>[] | WhereValue<TAttributes> | readonly WhereValue<TAttributes>[];
} }
/** Example: `[Op.and]: {a: 5}` becomes `AND (a = 5)` */ /** Example: `[Op.and]: {a: 5}` becomes `AND (a = 5)` */
export interface AndOperator<TAttributes = any> { export interface AndOperator<TAttributes = any> {
[Op.and]: WhereOptions<TAttributes> | WhereOptions<TAttributes>[] | WhereValue<TAttributes> | WhereValue<TAttributes>[]; [Op.and]: WhereOptions<TAttributes> | readonly WhereOptions<TAttributes>[] | WhereValue<TAttributes> | readonly WhereValue<TAttributes>[];
} }
/** /**
...@@ -324,7 +324,7 @@ export interface AndOperator<TAttributes = any> { ...@@ -324,7 +324,7 @@ export interface AndOperator<TAttributes = any> {
*/ */
export interface WhereGeometryOptions { export interface WhereGeometryOptions {
type: string; type: string;
coordinates: (number[] | number)[]; coordinates: readonly (number[] | number)[];
} }
/** /**
...@@ -345,7 +345,7 @@ export type WhereValue<TAttributes = any> = ...@@ -345,7 +345,7 @@ export type WhereValue<TAttributes = any> =
| OrOperator<TAttributes> | OrOperator<TAttributes>
| AndOperator<TAttributes> | AndOperator<TAttributes>
| WhereGeometryOptions | WhereGeometryOptions
| (string | number | Buffer | WhereAttributeHash<TAttributes>)[]; // implicit [Op.or] | readonly (string | number | Buffer | WhereAttributeHash<TAttributes>)[]; // implicit [Op.or]
/** /**
* A hash of attributes to describe your search. * A hash of attributes to describe your search.
...@@ -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?: Includeable[]; include?: readonly Includeable[];
/** /**
* Order include. Only available when setting `separate` to true. * Order include. Only available when setting `separate` to true.
...@@ -464,44 +464,44 @@ export type OrderItem = ...@@ -464,44 +464,44 @@ export type OrderItem =
| Fn | Fn
| Col | Col
| Literal | Literal
| [OrderItemColumn, string] | readonly [OrderItemColumn, string]
| [OrderItemAssociation, OrderItemColumn] | readonly [OrderItemAssociation, OrderItemColumn]
| [OrderItemAssociation, OrderItemColumn, string] | readonly [OrderItemAssociation, OrderItemColumn, string]
| [OrderItemAssociation, OrderItemAssociation, OrderItemColumn] | readonly [OrderItemAssociation, OrderItemAssociation, OrderItemColumn]
| [OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string] | readonly [OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string]
| [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn] | readonly [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn]
| [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string] | readonly [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string]
| [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn] | readonly [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn]
| [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string] | readonly [OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemAssociation, OrderItemColumn, string]
export type Order = string | Fn | Col | Literal | OrderItem[]; export type Order = string | Fn | Col | Literal | readonly 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
* as a property but only via `instance.get('alias')`. * as a property but only via `instance.get('alias')`.
*/ */
export type ProjectionAlias = [string | Literal | Fn, string]; export type ProjectionAlias = readonly [string | Literal | Fn, string];
export type FindAttributeOptions = export type FindAttributeOptions =
| (string | ProjectionAlias)[] | readonly (string | ProjectionAlias)[]
| { | {
exclude: string[]; exclude: readonly string[];
include?: (string | ProjectionAlias)[]; include?: readonly (string | ProjectionAlias)[];
} }
| { | {
exclude?: string[]; exclude?: readonly string[];
include: (string | ProjectionAlias)[]; include: readonly (string | ProjectionAlias)[];
}; };
export interface IndexHint { export interface IndexHint {
type: IndexHints; type: IndexHints;
values: string[]; values: readonly string[];
} }
export interface IndexHintable { export interface IndexHintable {
/** /**
* MySQL only. * MySQL only.
*/ */
indexHints?: IndexHint[]; indexHints?: readonly 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 | Includeable[]; include?: Includeable | readonly 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 | Includeable[]; include?: Includeable | readonly 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 | Includeable[]; include?: Includeable | readonly 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?: (keyof TAttributes)[]; fields?: readonly (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?: (keyof TAttributes)[]; fields?: readonly (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?: (keyof TAttributes)[]; fields?: readonly (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?: (keyof TAttributes)[]; fields?: readonly (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?: (keyof TAttributes)[]; updateOnDuplicate?: readonly (keyof TAttributes)[];
/** /**
* Include options. See `find` for details * Include options. See `find` for details
*/ */
include?: Includeable | Includeable[]; include?: Includeable | readonly 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 | (keyof TAttributes)[]; returning?: boolean | readonly (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?: (keyof TAttributes)[]; fields?: readonly (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?: (keyof TAttributes)[]; fields?: readonly (keyof TAttributes)[];
/** /**
* If false, validations won't be run. * If false, validations won't be run.
...@@ -990,15 +990,15 @@ export interface SaveOptions<TAttributes = any> extends Logging, Transactionable ...@@ -990,15 +990,15 @@ export interface SaveOptions<TAttributes = any> extends Logging, Transactionable
*/ */
export interface ModelValidateOptions { export interface ModelValidateOptions {
/** /**
* is: ["^[a-z]+$",'i'] // will only allow letters * - `{ is: ['^[a-z]+$','i'] }` will only allow letters
* is: /^[a-z]+[Op./i] // same as the previous example using real RegExp * - `{ is: /^[a-z]+$/i }` also only allows letters
*/ */
is?: string | (string | RegExp)[] | RegExp | { msg: string; args: string | (string | RegExp)[] | RegExp }; is?: string | readonly (string | RegExp)[] | RegExp | { msg: string; args: string | readonly (string | RegExp)[] | RegExp };
/** /**
* not: ["[a-z]",'i'] // will not allow letters * - `{ not: ['[a-z]','i'] }` will not allow letters
*/ */
not?: string | (string | RegExp)[] | RegExp | { msg: string; args: string | (string | RegExp)[] | RegExp }; not?: string | readonly (string | RegExp)[] | RegExp | { msg: string; args: string | readonly (string | RegExp)[] | RegExp };
/** /**
* checks for email format (foo@bar.com) * checks for email format (foo@bar.com)
...@@ -1093,22 +1093,22 @@ export interface ModelValidateOptions { ...@@ -1093,22 +1093,22 @@ export interface ModelValidateOptions {
/** /**
* check the value is not one of these * check the value is not one of these
*/ */
notIn?: string[][] | { msg: string; args: string[][] }; notIn?: ReadonlyArray<readonly string[]> | { msg: string; args: ReadonlyArray<readonly string[]> };
/** /**
* check the value is one of these * check the value is one of these
*/ */
isIn?: string[][] | { msg: string; args: string[][] }; isIn?: ReadonlyArray<readonly string[]> | { msg: string; args: ReadonlyArray<readonly string[]> };
/** /**
* don't allow specific substrings * don't allow specific substrings
*/ */
notContains?: string[] | string | { msg: string; args: string[] | string }; notContains?: readonly string[] | string | { msg: string; args: readonly string[] | string };
/** /**
* only allow values with length between 2 and 10 * only allow values with length between 2 and 10
*/ */
len?: [number, number] | { msg: string; args: [number, number] }; len?: readonly [number, number] | { msg: string; args: readonly [number, number] };
/** /**
* only allow uuids * only allow uuids
...@@ -1133,12 +1133,12 @@ export interface ModelValidateOptions { ...@@ -1133,12 +1133,12 @@ export interface ModelValidateOptions {
/** /**
* only allow values * only allow values
*/ */
max?: number | { msg: string; args: [number] }; max?: number | { msg: string; args: readonly [number] };
/** /**
* only allow values >= 23 * only allow values >= 23
*/ */
min?: number | { msg: string; args: [number] }; min?: number | { msg: string; args: readonly [number] };
/** /**
* only allow arrays * only allow arrays
...@@ -1150,20 +1150,10 @@ export interface ModelValidateOptions { ...@@ -1150,20 +1150,10 @@ export interface ModelValidateOptions {
*/ */
isCreditCard?: boolean | { msg: string; args: boolean }; isCreditCard?: boolean | { msg: string; args: boolean };
// TODO: Enforce 'rest' indexes to have type `(value: unknown) => boolean`
// Blocked by: https://github.com/microsoft/TypeScript/issues/7765
/** /**
* custom validations are also possible * Custom validations are also possible
*
* Implementation notes :
*
* We can't enforce any other method to be a function, so :
*
* ```typescript
* [name: string] : ( value : unknown ) => boolean;
* ```
*
* doesn't work in combination with the properties above
*
* @see https://github.com/Microsoft/TypeScript/issues/1889
*/ */
[name: string]: unknown; [name: string]: unknown;
} }
...@@ -1209,7 +1199,7 @@ export interface ModelScopeOptions<TAttributes = any> { ...@@ -1209,7 +1199,7 @@ export interface ModelScopeOptions<TAttributes = any> {
/** /**
* Name of the scope and it's query * Name of the scope and it's query
*/ */
[scopeName: string]: FindOptions<TAttributes> | ((...args: any[]) => FindOptions<TAttributes>); [scopeName: string]: FindOptions<TAttributes> | ((...args: readonly any[]) => FindOptions<TAttributes>);
} }
/** /**
...@@ -1334,7 +1324,7 @@ export interface ModelAttributeColumnOptions<M extends Model = Model> extends Co ...@@ -1334,7 +1324,7 @@ export interface ModelAttributeColumnOptions<M extends Model = Model> extends Co
* }, { sequelize }) * }, { sequelize })
* ``` * ```
*/ */
values?: string[]; values?: readonly string[];
/** /**
* Provide a custom getter for this column. Use `this.getDataValue(String)` to manipulate the underlying * Provide a custom getter for this column. Use `this.getDataValue(String)` to manipulate the underlying
...@@ -1426,7 +1416,7 @@ export interface ModelOptions<M extends Model = Model> { ...@@ -1426,7 +1416,7 @@ export interface ModelOptions<M extends Model = Model> {
/** /**
* Indexes for the provided database table * Indexes for the provided database table
*/ */
indexes?: ModelIndexesOptions[]; indexes?: readonly ModelIndexesOptions[];
/** /**
* Override the name of the createdAt column if a string is provided, or disable it if false. Timestamps * Override the name of the createdAt column if a string is provided, or disable it if false. Timestamps
...@@ -1564,7 +1554,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut ...@@ -1564,7 +1554,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut
/** /**
* The name of the primary key attributes * The name of the primary key attributes
*/ */
public static readonly primaryKeyAttributes: string[]; public static readonly primaryKeyAttributes: readonly string[];
/** /**
* An object hash from alias to association object * An object hash from alias to association object
...@@ -1737,7 +1727,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut ...@@ -1737,7 +1727,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut
*/ */
public static scope<M extends Model>( public static scope<M extends Model>(
this: ModelStatic<M>, this: ModelStatic<M>,
options?: string | ScopeOptions | (string | ScopeOptions)[] | WhereAttributeHash<M> options?: string | ScopeOptions | readonly (string | ScopeOptions)[] | WhereAttributeHash<M>
): ModelCtor<M>; ): ModelCtor<M>;
/** /**
...@@ -1757,7 +1747,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut ...@@ -1757,7 +1747,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut
public static addScope<M extends Model>( public static addScope<M extends Model>(
this: ModelStatic<M>, this: ModelStatic<M>,
name: string, name: string,
scope: (...args: any[]) => FindOptions<M['_attributes']>, scope: (...args: readonly any[]) => FindOptions<M['_attributes']>,
options?: AddScopeOptions options?: AddScopeOptions
): void; ): void;
...@@ -1969,7 +1959,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut ...@@ -1969,7 +1959,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut
*/ */
public static bulkBuild<M extends Model>( public static bulkBuild<M extends Model>(
this: ModelStatic<M>, this: ModelStatic<M>,
records: (M['_creationAttributes'])[], records: ReadonlyArray<M['_creationAttributes']>,
options?: BuildOptions options?: BuildOptions
): M[]; ): M[];
...@@ -2059,7 +2049,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut ...@@ -2059,7 +2049,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut
*/ */
public static bulkCreate<M extends Model>( public static bulkCreate<M extends Model>(
this: ModelStatic<M>, this: ModelStatic<M>,
records: (M['_creationAttributes'])[], records: ReadonlyArray<M['_creationAttributes']>,
options?: BulkCreateOptions<M['_attributes']> options?: BulkCreateOptions<M['_attributes']>
): Promise<M[]>; ): Promise<M[]>;
...@@ -2114,7 +2104,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut ...@@ -2114,7 +2104,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut
*/ */
public static increment<M extends Model>( public static increment<M extends Model>(
this: ModelStatic<M>, this: ModelStatic<M>,
fields: (keyof M['_attributes'])[], fields: ReadonlyArray<keyof M['_attributes']>,
options: IncrementDecrementOptionsWithBy<M['_attributes']> options: IncrementDecrementOptionsWithBy<M['_attributes']>
): Promise<M>; ): Promise<M>;
...@@ -2307,11 +2297,11 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut ...@@ -2307,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: M[], options: BulkCreateOptions<M['_attributes']>) => HookReturn fn: (instances: readonly 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: M[], options: BulkCreateOptions<M['_attributes']>) => HookReturn fn: (instances: readonly M[], options: BulkCreateOptions<M['_attributes']>) => HookReturn
): void; ): void;
/** /**
...@@ -2323,11 +2313,11 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut ...@@ -2323,11 +2313,11 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut
public static afterBulkCreate<M extends Model>( public static afterBulkCreate<M extends Model>(
this: ModelStatic<M>, this: ModelStatic<M>,
name: string, name: string,
fn: (instances: M[], options: BulkCreateOptions<M['_attributes']>) => HookReturn fn: (instances: readonly M[], options: BulkCreateOptions<M['_attributes']>) => HookReturn
): void; ): void;
public static afterBulkCreate<M extends Model>( public static afterBulkCreate<M extends Model>(
this: ModelStatic<M>, this: ModelStatic<M>,
fn: (instances: M[], options: BulkCreateOptions<M['_attributes']>) => HookReturn fn: (instances: readonly M[], options: BulkCreateOptions<M['_attributes']>) => HookReturn
): void; ): void;
/** /**
...@@ -2458,11 +2448,11 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut ...@@ -2458,11 +2448,11 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut
public static afterFind<M extends Model>( public static afterFind<M extends Model>(
this: ModelStatic<M>, this: ModelStatic<M>,
name: string, name: string,
fn: (instancesOrInstance: M[] | M | null, options: FindOptions<M['_attributes']>) => HookReturn fn: (instancesOrInstance: readonly M[] | M | null, options: FindOptions<M['_attributes']>) => HookReturn
): void; ): void;
public static afterFind<M extends Model>( public static afterFind<M extends Model>(
this: ModelStatic<M>, this: ModelStatic<M>,
fn: (instancesOrInstance: M[] | M | null, options: FindOptions<M['_attributes']>) => HookReturn fn: (instancesOrInstance: readonly M[] | M | null, options: FindOptions<M['_attributes']>) => HookReturn
): void; ): void;
/** /**
...@@ -2787,7 +2777,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut ...@@ -2787,7 +2777,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut
* If and object is provided, each column is incremented by the value given. * If and object is provided, each column is incremented by the value given.
*/ */
public increment<K extends keyof TModelAttributes>( public increment<K extends keyof TModelAttributes>(
fields: K | K[] | Partial<TModelAttributes>, fields: K | readonly K[] | Partial<TModelAttributes>,
options?: IncrementDecrementOptionsWithBy<TModelAttributes> options?: IncrementDecrementOptionsWithBy<TModelAttributes>
): Promise<this>; ): Promise<this>;
...@@ -2812,7 +2802,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut ...@@ -2812,7 +2802,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut
* If and object is provided, each column is decremented by the value given * If and object is provided, each column is decremented by the value given
*/ */
public decrement<K extends keyof TModelAttributes>( public decrement<K extends keyof TModelAttributes>(
fields: K | K[] | Partial<TModelAttributes>, fields: K | readonly K[] | Partial<TModelAttributes>,
options?: IncrementDecrementOptionsWithBy<TModelAttributes> options?: IncrementDecrementOptionsWithBy<TModelAttributes>
): Promise<this>; ): Promise<this>;
...@@ -2824,7 +2814,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut ...@@ -2824,7 +2814,7 @@ export abstract class Model<TModelAttributes extends {} = any, TCreationAttribut
/** /**
* Check if this is eqaul to one of `others` by calling equals * Check if this is eqaul to one of `others` by calling equals
*/ */
public equalsOneOf(others: this[]): boolean; public equalsOneOf(others: readonly this[]): boolean;
/** /**
* Convert the instance to a JSON representation. Proxies to calling `get` with no keys. This means get all * Convert the instance to a JSON representation. Proxies to calling `get` with no keys. This means get all
......
import {Model, SaveOptions, Sequelize, FindOptions} from "sequelize" import { expectTypeOf } from "expect-type";
import { Model, SaveOptions, Sequelize, FindOptions } from "sequelize";
import { ModelHooks } from "../lib/hooks"; import { ModelHooks } from "../lib/hooks";
/* class TestModel extends Model {}
* covers types/lib/sequelize.d.ts
*/
Sequelize.beforeSave((t: TestModel, options: SaveOptions) => {});
Sequelize.afterSave((t: TestModel, options: SaveOptions) => {});
Sequelize.afterFind((t: TestModel[] | TestModel | null, options: FindOptions) => {});
Sequelize.afterFind('namedAfterFind', (t: TestModel[] | TestModel | null, options: FindOptions) => {});
/* /*
* covers types/lib/hooks.d.ts * covers types/lib/hooks.d.ts
*/ */
export const sequelize = new Sequelize('uri', {
hooks: {
beforeSave (m: Model, options: SaveOptions) {},
afterSave (m: Model, options: SaveOptions) {},
afterFind (m: Model[] | Model | null, options: FindOptions) {},
}
});
class TestModel extends Model {
}
const hooks: Partial<ModelHooks> = { const hooks: Partial<ModelHooks> = {
beforeSave(t: TestModel, options: SaveOptions) { }, beforeSave(m, options) {
afterSave(t: TestModel, options: SaveOptions) { }, expectTypeOf(m).toEqualTypeOf<TestModel>();
afterFind(t: TestModel | TestModel[] | null, options: FindOptions) { }, 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>();
}
}; };
TestModel.init({}, {sequelize, hooks }) export const sequelize = new Sequelize('uri', { hooks });
TestModel.init({}, { sequelize, hooks });
TestModel.addHook('beforeSave', (t: TestModel, options: SaveOptions) => { }); TestModel.addHook('beforeSave', hooks.beforeSave!);
TestModel.addHook('afterSave', (t: TestModel, options: SaveOptions) => { }); TestModel.addHook('afterSave', hooks.afterSave!);
TestModel.addHook('afterFind', (t: TestModel[] | TestModel | null, options: FindOptions) => { }); TestModel.addHook('afterFind', hooks.afterFind!);
/* /*
* covers types/lib/model.d.ts * covers types/lib/model.d.ts
*/ */
TestModel.beforeSave((t: TestModel, options: SaveOptions) => { }); TestModel.beforeSave(hooks.beforeSave!);
TestModel.afterSave((t: TestModel, options: SaveOptions) => { }); TestModel.afterSave(hooks.afterSave!);
TestModel.afterFind((t: TestModel | TestModel[] | null, options: FindOptions) => { }); 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!);
import { expectTypeOf } from "expect-type";
import { AndOperator, fn, Model, Op, OrOperator, Sequelize, WhereOperators, WhereOptions, literal, where as whereFn } from 'sequelize'; import { AndOperator, fn, Model, Op, OrOperator, Sequelize, WhereOperators, WhereOptions, literal, where as whereFn } from 'sequelize';
import Transaction from '../lib/transaction'; import Transaction from '../lib/transaction';
...@@ -5,15 +6,9 @@ class MyModel extends Model { ...@@ -5,15 +6,9 @@ class MyModel extends Model {
public hi!: number; public hi!: number;
} }
let where: WhereOptions; // Simple options
// From https://sequelize.org/master/en/v4/docs/querying/ expectTypeOf({
/**
* Literal values
* @see WhereValue
*/
where = {
string: 'foo', string: 'foo',
strings: ['foo'], strings: ['foo'],
number: 1, number: 1,
...@@ -23,34 +18,32 @@ where = { ...@@ -23,34 +18,32 @@ where = {
buffers: [Buffer.alloc(0)], buffers: [Buffer.alloc(0)],
null: null, null: null,
date: new Date() date: new Date()
}; }).toMatchTypeOf<WhereOptions>();
// Optional values // Optional values
let whereWithOptionals: { needed: number; optional?: number } = { needed: 2 }; expectTypeOf<{ needed: number; optional?: number }>().toMatchTypeOf<WhereOptions>();
where = whereWithOptionals;
// Misusing optional values (typings allow this, sequelize will throw an error during runtime) // Misusing optional values (typings allow this, sequelize will throw an error during runtime)
// This might be solved by updates to typescript itself (https://github.com/microsoft/TypeScript/issues/13195) // This might be solved by updates to typescript itself (https://github.com/microsoft/TypeScript/issues/13195)
whereWithOptionals = { needed: 2, optional: undefined }; // expectTypeOf({ needed: 2, optional: undefined }).not.toMatchTypeOf<WhereOptions>();
where = whereWithOptionals;
// Operators // Operators
const and: AndOperator = { expectTypeOf({
[Op.and]: { a: 5 }, // AND (a = 5) [Op.and]: { a: 5 }, // AND (a = 5)
}; }).toMatchTypeOf<AndOperator>();
const typedAnd: AndOperator<{ a: number }> = { expectTypeOf({
[Op.and]: { a: 5 }, // AND (a = 5) [Op.and]: { a: 5 }, // AND (a = 5)
}; }).toMatchTypeOf<AndOperator<{ a: number }>>();
const or: OrOperator = { expectTypeOf({
[Op.or]: [{ a: 5 }, { a: 6 }], // (a = 5 OR a = 6) [Op.or]: [{ a: 5 }, { a: 6 }], // (a = 5 OR a = 6)
}; }).toMatchTypeOf<OrOperator>();
const typedOr: OrOperator<{ a: number }> = { expectTypeOf({
[Op.or]: [{ a: 5 }, { a: 6 }], // (a = 5 OR a = 6) [Op.or]: [{ a: 5 }, { a: 6 }], // (a = 5 OR a = 6)
}; }).toMatchTypeOf<OrOperator<{ a: number }>>();
let operators: WhereOperators = { expectTypeOf({
[Op.gt]: 6, // > 6 [Op.gt]: 6, // > 6
[Op.gte]: 6, // >= 6 [Op.gte]: 6, // >= 6
[Op.lt]: 10, // < 10 [Op.lt]: 10, // < 10
...@@ -76,74 +69,44 @@ let operators: WhereOperators = { ...@@ -76,74 +69,44 @@ let operators: WhereOperators = {
[Op.notRegexp]: '^[h|a|t]', // NOT REGEXP/!~ '^[h|a|t]' (MySQL/PG only) [Op.notRegexp]: '^[h|a|t]', // NOT REGEXP/!~ '^[h|a|t]' (MySQL/PG only)
[Op.iRegexp]: '^[h|a|t]', // ~* '^[h|a|t]' (PG only) [Op.iRegexp]: '^[h|a|t]', // ~* '^[h|a|t]' (PG only)
[Op.notIRegexp]: '^[h|a|t]' // !~* '^[h|a|t]' (PG only) [Op.notIRegexp]: '^[h|a|t]' // !~* '^[h|a|t]' (PG only)
}; } as const).toMatchTypeOf<WhereOperators>();
operators = { expectTypeOf({
[Op.like]: { [Op.any]: ['cat', 'hat'] }, // LIKE ANY ARRAY['cat', 'hat'] - also works for iLike and notLike [Op.like]: { [Op.any]: ['cat', 'hat'] }, // LIKE ANY ARRAY['cat', 'hat']
}; [Op.iLike]: { [Op.any]: ['cat', 'hat'] }, // LIKE ANY ARRAY['cat', 'hat']
[Op.notLike]: { [Op.any]: ['cat', 'hat'] }, // LIKE ANY ARRAY['cat', 'hat']
// Combinations [Op.notILike]: { [Op.any]: ['cat', 'hat'] }, // LIKE ANY ARRAY['cat', 'hat']
}).toMatchTypeOf<WhereOperators>();
MyModel.findOne({ where: or });
MyModel.findOne({ where: and }); // Complex where options via combinations
where = Sequelize.and(); expectTypeOf([
{ [Op.or]: [{ a: 5 }, { a: 6 }] },
where = Sequelize.or(); Sequelize.and(),
Sequelize.or(),
where = { [Op.and]: [] }; { [Op.and]: [] },
{ rank: Sequelize.and({ [Op.lt]: 1000 }, { [Op.eq]: null }) },
where = { { rank: Sequelize.or({ [Op.lt]: 1000 }, { [Op.eq]: null }) },
rank: Sequelize.and({ [Op.lt]: 1000 }, { [Op.eq]: null }), { rank: { [Op.or]: { [Op.lt]: 1000, [Op.eq]: null } } },
}; {
where = {
rank: Sequelize.or({ [Op.lt]: 1000 }, { [Op.eq]: null }),
};
where = {
rank: {
[Op.or]: {
[Op.lt]: 1000,
[Op.eq]: null,
},
},
};
// rank < 1000 OR rank IS NULL
where = {
createdAt: { createdAt: {
[Op.lt]: new Date(), [Op.lt]: new Date(),
[Op.gt]: new Date(Date.now() - 24 * 60 * 60 * 1000), [Op.gt]: new Date(Date.now() - 24 * 60 * 60 * 1000),
}
}, },
};
// createdAt < [timestamp] AND createdAt > [timestamp]
where = {
[Op.or]: [
{ {
title: { [Op.or]: [
[Op.like]: 'Boat%', { title: { [Op.like]: 'Boat%' } },
}, { description: { [Op.like]: '%boat%' } }
]
}, },
{ {
description: {
[Op.like]: '%boat%',
},
},
],
};
// title LIKE 'Boat%' OR description LIKE '%boat%'
// Containment
where = {
meta: { meta: {
[Op.contains]: { [Op.contains]: {
site: { site: {
url: 'http://google.com', url: 'https://sequelize.org/'
}, }
}, }
}, },
meta2: { meta2: {
[Op.contains]: ['stringValue1', 'stringValue2', 'stringValue3'] [Op.contains]: ['stringValue1', 'stringValue2', 'stringValue3']
...@@ -151,7 +114,58 @@ where = { ...@@ -151,7 +114,58 @@ where = {
meta3: { meta3: {
[Op.contains]: [1, 2, 3, 4] [Op.contains]: [1, 2, 3, 4]
}, },
}; },
{
name: 'a project',
[Op.or]: [{ id: [1, 2, 3] }, { id: { [Op.gt]: 10 } }]
},
{
id: {
[Op.or]: [[1, 2, 3], { [Op.gt]: 10 }]
},
name: 'a project'
},
{
id: {
[Op.or]: [[1, 2, 3], { [Op.gt]: 10 }]
},
name: 'a project'
},
{
name: 'a project',
type: {
[Op.and]: [['a', 'b'], { [Op.notLike]: '%z' }],
},
},
{
name: 'a project',
[Op.not]: [{ id: [1, 2, 3] }, { array: { [Op.contains]: [3, 4, 5] } }],
},
{
meta: {
video: {
url: {
[Op.ne]: null,
},
},
},
},
{
'meta.audio.length': {
[Op.gt]: 20,
},
},
{
[Op.and]: [{ id: [1, 2, 3] }, { array: { [Op.contains]: [3, 4, 5] } }],
},
{
[Op.gt]: fn('NOW'),
},
whereFn('test', { [Op.gt]: new Date() }),
literal('true'),
fn('LOWER', 'asd'),
{ [Op.lt]: Sequelize.literal('SOME_STRING') }
]).toMatchTypeOf<WhereOptions[]>();
// Relations / Associations // Relations / Associations
// Find all projects with a least one task where task.state === project.task // Find all projects with a least one task where task.state === project.task
...@@ -164,7 +178,9 @@ MyModel.findAll({ ...@@ -164,7 +178,9 @@ MyModel.findAll({
], ],
}); });
MyModel.findOne({ {
const where: WhereOptions = 0 as any;
MyModel.findOne({
include: [ include: [
{ {
include: [{ model: MyModel, where }], include: [{ model: MyModel, where }],
...@@ -173,9 +189,13 @@ MyModel.findOne({ ...@@ -173,9 +189,13 @@ MyModel.findOne({
}, },
], ],
where, where,
}); });
MyModel.destroy({ where }); MyModel.destroy({ where });
MyModel.update({ hi: 1 }, { where }); MyModel.update({ hi: 1 }, { where });
// Where as having option
MyModel.findAll({ having: where });
}
// From https://sequelize.org/master/en/v4/docs/models-usage/ // From https://sequelize.org/master/en/v4/docs/models-usage/
...@@ -232,86 +252,6 @@ MyModel.findAll({ ...@@ -232,86 +252,6 @@ MyModel.findAll({
}, },
}); });
// Complex filtering / NOT queries
where = {
name: 'a project',
[Op.or]: [{ id: [1, 2, 3] }, { id: { [Op.gt]: 10 } }],
};
where = {
id: {
[Op.or]: [[1, 2, 3], { [Op.gt]: 10 }],
},
name: 'a project',
};
where = {
name: 'a project',
type: {
[Op.and]: [['a', 'b'], { [Op.notLike]: '%z' }],
},
};
// [Op.not] example:
where = {
name: 'a project',
[Op.not]: [{ id: [1, 2, 3] }, { array: { [Op.contains]: [3, 4, 5] } }],
};
// JSONB
// Nested object
where = {
meta: {
video: {
url: {
[Op.ne]: null,
},
},
},
};
// Nested key
where = {
'meta.audio.length': {
[Op.gt]: 20,
},
};
// Operator symbols
where = {
[Op.and]: [{ id: [1, 2, 3] }, { array: { [Op.contains]: [3, 4, 5] } }],
};
// Fn as value
where = {
[Op.gt]: fn('NOW'),
};
where = whereFn('test', {
[Op.gt]: new Date(),
});
// Literal as where
where = literal('true')
where = fn('LOWER', 'asd')
MyModel.findAll({
where: literal('true')
})
// Where as having option
MyModel.findAll({
having: where
});
where = {
[Op.lt]: Sequelize.literal('SOME_STRING')
}
Sequelize.where( Sequelize.where(
Sequelize.cast(Sequelize.col('SOME_COL'), 'INTEGER'), { Sequelize.cast(Sequelize.col('SOME_COL'), 'INTEGER'), {
[Op.lt]: Sequelize.literal('LIT'), [Op.lt]: Sequelize.literal('LIT'),
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!