Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix typeof Model errors throughout by using typeof Model generics #900

Merged
merged 9 commits into from
Feb 14, 2021
15 changes: 8 additions & 7 deletions src/associations/belongs-to-many/belongs-to-many-association.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {BelongsToManyOptions as OriginBelongsToManyOptions, Model, ThroughOptions} from "sequelize";
import {BelongsToManyOptions as OriginBelongsToManyOptions, ThroughOptions} from "sequelize";

import {BaseAssociation} from '../shared/base-association';
import {BelongsToManyOptions} from './belongs-to-many-options';
Expand All @@ -8,28 +8,29 @@ import {ModelClassGetter} from "../../model/shared/model-class-getter";
import {Association} from "../shared/association";
import {Sequelize} from "../../sequelize/sequelize/sequelize";
import {UnionAssociationOptions} from "../shared/union-association-options";
import {ModelType} from "../../model/model/model";

export class BelongsToManyAssociation extends BaseAssociation {
export class BelongsToManyAssociation<TCreationAttributes, TModelAttributes> extends BaseAssociation<TCreationAttributes, TModelAttributes> {

constructor(associatedClassGetter: ModelClassGetter,
protected options: BelongsToManyOptions) {
constructor(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>,
protected options: BelongsToManyOptions<TCreationAttributes, TModelAttributes>) {
super(associatedClassGetter, options);
}

getAssociation(): Association {
return Association.BelongsToMany;
}

getSequelizeOptions(model: typeof Model,
getSequelizeOptions(model: ModelType<TCreationAttributes, TModelAttributes>,
sequelize: Sequelize): UnionAssociationOptions {
const options: OriginBelongsToManyOptions = {...this.options as any};
const associatedClass = this.getAssociatedClass();
const throughOptions = this.getThroughOptions(sequelize);

const throughModel = typeof throughOptions === 'object' && typeof throughOptions.model !== "string" ? throughOptions.model : undefined;
options.through = throughOptions;
options.foreignKey = getForeignKeyOptions(model, throughModel, this.options.foreignKey);
options.otherKey = getForeignKeyOptions(associatedClass, throughModel, this.options.otherKey);
options.foreignKey = getForeignKeyOptions(model, throughModel as any, this.options.foreignKey);
options.otherKey = getForeignKeyOptions(associatedClass, throughModel as any, this.options.otherKey);
theoludwig marked this conversation as resolved.
Show resolved Hide resolved

return options;
}
Expand Down
4 changes: 2 additions & 2 deletions src/associations/belongs-to-many/belongs-to-many-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import {ModelClassGetter} from "../../model/shared/model-class-getter";
import {ThroughOptions} from "../through/through-options";


export type BelongsToManyOptions = {
export type BelongsToManyOptions<TCreationAttributes, TModelAttributes> = {
[K in keyof OriginBelongsToManyOptions]: K extends 'through'
? ModelClassGetter | string | ThroughOptions
? ModelClassGetter<TCreationAttributes, TModelAttributes> | string | ThroughOptions<TCreationAttributes, TModelAttributes>
: OriginBelongsToManyOptions[K]
};
24 changes: 12 additions & 12 deletions src/associations/belongs-to-many/belongs-to-many.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ import {BelongsToManyAssociation} from './belongs-to-many-association';
import {ModelClassGetter} from "../../model/shared/model-class-getter";
import {addAssociation} from "../shared/association-service";

export function BelongsToMany(associatedClassGetter: ModelClassGetter,
through: ModelClassGetter | string,
foreignKey?: string,
otherKey?: string): Function;
export function BelongsToMany(associatedClassGetter: ModelClassGetter,
options: BelongsToManyOptions): Function;
export function BelongsToMany(associatedClassGetter: ModelClassGetter,
throughOrOptions: ModelClassGetter | string | BelongsToManyOptions,
foreignKey?: string,
otherKey?: string): Function {
export function BelongsToMany<TCreationAttributes, TModelAttributes>(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>,
through: ModelClassGetter<TCreationAttributes, TModelAttributes> | string,
foreignKey?: string,
otherKey?: string): Function;
export function BelongsToMany<TCreationAttributes, TModelAttributes>(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>,
options: BelongsToManyOptions<TCreationAttributes, TModelAttributes>): Function;
export function BelongsToMany<TCreationAttributes, TModelAttributes>(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>,
throughOrOptions: ModelClassGetter<TCreationAttributes, TModelAttributes> | string | BelongsToManyOptions<TCreationAttributes, TModelAttributes>,
foreignKey?: string,
otherKey?: string): Function {

return (target: any, propertyName: string) => {
let options: Partial<BelongsToManyOptions> = {foreignKey, otherKey};
let options: Partial<BelongsToManyOptions<TCreationAttributes, TModelAttributes>> = {foreignKey, otherKey};

if (typeof throughOrOptions === 'string' ||
typeof throughOrOptions === 'function') {
Expand All @@ -28,7 +28,7 @@ export function BelongsToMany(associatedClassGetter: ModelClassGetter,

addAssociation(target, new BelongsToManyAssociation(
associatedClassGetter,
options as BelongsToManyOptions,
options as BelongsToManyOptions<TCreationAttributes, TModelAttributes>,
)
);
};
Expand Down
8 changes: 4 additions & 4 deletions src/associations/belongs-to/belongs-to-association.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import {BaseAssociation} from '../shared/base-association';
import {getForeignKeyOptions} from "../foreign-key/foreign-key-service";
import {ModelClassGetter} from "../../model/shared/model-class-getter";
import {Association} from "../shared/association";
import {Model} from "../../model/model/model";
import {ModelType} from "../../model/model/model";
import {UnionAssociationOptions} from "../shared/union-association-options";

export class BelongsToAssociation extends BaseAssociation {
export class BelongsToAssociation<TCreationAttributes, TModelAttributes> extends BaseAssociation<TCreationAttributes, TModelAttributes> {

constructor(associatedClassGetter: ModelClassGetter,
constructor(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>,
protected options: BelongsToOptions) {
super(associatedClassGetter, options);
}
Expand All @@ -18,7 +18,7 @@ export class BelongsToAssociation extends BaseAssociation {
return Association.BelongsTo;
}

getSequelizeOptions(model: typeof Model): UnionAssociationOptions {
getSequelizeOptions(model: ModelType<TCreationAttributes, TModelAttributes>): UnionAssociationOptions {
const associatedClass = this.getAssociatedClass();
const foreignKey = getForeignKeyOptions(associatedClass, model, this.options.foreignKey);

Expand Down
6 changes: 3 additions & 3 deletions src/associations/belongs-to/belongs-to.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import {BelongsToAssociation} from './belongs-to-association';
import {ModelClassGetter} from "../../model/shared/model-class-getter";
import {addAssociation, getPreparedAssociationOptions} from "../shared/association-service";

export function BelongsTo(associatedClassGetter: ModelClassGetter, foreignKey?: string): Function;
export function BelongsTo<TCreationAttributes, TModelAttributes>(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>, foreignKey?: string): Function;

export function BelongsTo(associatedClassGetter: ModelClassGetter, options?: BelongsToOptions): Function;
export function BelongsTo<TCreationAttributes, TModelAttributes>(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>, options?: BelongsToOptions): Function;

export function BelongsTo(associatedClassGetter: ModelClassGetter, optionsOrForeignKey?: string | BelongsToOptions): Function {
export function BelongsTo<TCreationAttributes, TModelAttributes>(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>, optionsOrForeignKey?: string | BelongsToOptions): Function {

return (target: any, propertyName: string) => {
const options: BelongsToOptions = getPreparedAssociationOptions(optionsOrForeignKey);
Expand Down
4 changes: 2 additions & 2 deletions src/associations/foreign-key/foreign-key-meta.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {ModelClassGetter} from "../../model/shared/model-class-getter";

export interface ForeignKeyMeta {
export interface ForeignKeyMeta<TCreationAttributes, TModelAttributes> {

relatedClassGetter: ModelClassGetter;
relatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>;
foreignKey: string;
}
17 changes: 9 additions & 8 deletions src/associations/foreign-key/foreign-key-service.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import {ForeignKeyOptions, Model} from "sequelize";
import {ForeignKeyOptions} from "sequelize";

import {ForeignKeyMeta} from './foreign-key-meta';
import {ModelClassGetter} from "../../model/shared/model-class-getter";
import {ModelType} from "../../model/model/model";

const FOREIGN_KEYS_KEY = 'sequelize:foreignKeys';

export function getForeignKeyOptions(relatedClass: typeof Model,
classWithForeignKey?: typeof Model,
foreignKey?: string | ForeignKeyOptions): ForeignKeyOptions {
export function getForeignKeyOptions<TCreationAttributes, TModelAttributes>(relatedClass: ModelType<TCreationAttributes, TModelAttributes>,
classWithForeignKey?: ModelType<TCreationAttributes, TModelAttributes>,
foreignKey?: string | ForeignKeyOptions): ForeignKeyOptions {
let foreignKeyOptions: ForeignKeyOptions = {};

if (typeof foreignKey === 'string') {
Expand Down Expand Up @@ -36,9 +37,9 @@ export function getForeignKeyOptions(relatedClass: typeof Model,
/**
* Adds foreign key meta data for specified class
*/
export function addForeignKey(target: any,
relatedClassGetter: ModelClassGetter,
foreignKey: string): void {
export function addForeignKey<TCreationAttributes, TModelAttributes>(target: any,
relatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>,
foreignKey: string): void {
let foreignKeys = getForeignKeys(target);
if (!foreignKeys) {
foreignKeys = [];
Expand All @@ -53,7 +54,7 @@ export function addForeignKey(target: any,
/**
* Returns foreign key meta data from specified class
*/
export function getForeignKeys(target: any): ForeignKeyMeta[] | undefined {
export function getForeignKeys<TCreationAttributes, TModelAttributes>(target: any): ForeignKeyMeta<TCreationAttributes, TModelAttributes>[] | undefined {
const foreignKeys = Reflect.getMetadata(FOREIGN_KEYS_KEY, target);
if (foreignKeys) {
return [...foreignKeys];
Expand Down
2 changes: 1 addition & 1 deletion src/associations/foreign-key/foreign-key.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {addForeignKey} from "./foreign-key-service";
import {ModelClassGetter} from "../../model/shared/model-class-getter";

export function ForeignKey(relatedClassGetter: ModelClassGetter): Function {
export function ForeignKey<TCreationAttributes, TModelAttributes>(relatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>): Function {
return (target: any, propertyName: string) => {
addForeignKey(target, relatedClassGetter, propertyName);
};
Expand Down
9 changes: 5 additions & 4 deletions src/associations/has/has-association.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import {HasManyOptions, HasOneOptions, Model} from 'sequelize';
import {HasManyOptions, HasOneOptions} from 'sequelize';

import {BaseAssociation} from '../shared/base-association';
import {getForeignKeyOptions} from "../foreign-key/foreign-key-service";
import {ModelClassGetter} from "../../model/shared/model-class-getter";
import {Association} from "../shared/association";
import {UnionAssociationOptions} from "../shared/union-association-options";
import {ModelType} from '../../model/model/model';

export class HasAssociation extends BaseAssociation {
export class HasAssociation<TCreationAttributes, TModelAttributes> extends BaseAssociation<TCreationAttributes, TModelAttributes> {

constructor(associatedClassGetter: ModelClassGetter,
constructor(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>,
protected options: HasManyOptions | HasOneOptions,
private association: Association) {
super(associatedClassGetter, options);
Expand All @@ -18,7 +19,7 @@ export class HasAssociation extends BaseAssociation {
return this.association;
}

getSequelizeOptions(model: typeof Model): UnionAssociationOptions {
getSequelizeOptions(model: ModelType<TCreationAttributes, TModelAttributes>): UnionAssociationOptions {
const options = {...this.options};
const associatedClass = this.getAssociatedClass();

Expand Down
6 changes: 3 additions & 3 deletions src/associations/has/has-many.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import {ModelClassGetter} from "../../model/shared/model-class-getter";
import {addAssociation, getPreparedAssociationOptions} from "../shared/association-service";
import {Association} from "../shared/association";

export function HasMany(associatedClassGetter: ModelClassGetter, foreignKey?: string): Function;
export function HasMany<TCreationAttributes, TModelAttributes>(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>, foreignKey?: string): Function;

export function HasMany(associatedClassGetter: ModelClassGetter, options?: HasManyOptions): Function;
export function HasMany<TCreationAttributes, TModelAttributes>(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>, options?: HasManyOptions): Function;

export function HasMany(associatedClassGetter: ModelClassGetter, optionsOrForeignKey?: string | HasManyOptions): Function {
export function HasMany<TCreationAttributes, TModelAttributes>(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>, optionsOrForeignKey?: string | HasManyOptions): Function {

return (target: any, propertyName: string) => {
const options: HasManyOptions = getPreparedAssociationOptions(optionsOrForeignKey);
Expand Down
6 changes: 3 additions & 3 deletions src/associations/has/has-one.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import {ModelClassGetter} from "../../model/shared/model-class-getter";
import {addAssociation, getPreparedAssociationOptions} from "../shared/association-service";
import {Association} from "../shared/association";

export function HasOne(associatedClassGetter: ModelClassGetter, foreignKey?: string): Function;
export function HasOne<TCreationAttributes, TModelAttributes>(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>, foreignKey?: string): Function;

export function HasOne(associatedClassGetter: ModelClassGetter, options?: HasOneOptions): Function;
export function HasOne<TCreationAttributes, TModelAttributes>(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>, options?: HasOneOptions): Function;

export function HasOne(associatedClassGetter: ModelClassGetter, optionsOrForeignKey?: string | HasOneOptions): Function {
export function HasOne<TCreationAttributes, TModelAttributes>(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>, optionsOrForeignKey?: string | HasOneOptions): Function {

return (target: any, propertyName: string) => {
const options: HasOneOptions = getPreparedAssociationOptions(optionsOrForeignKey);
Expand Down
14 changes: 7 additions & 7 deletions src/associations/shared/association-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ export function getPreparedAssociationOptions(optionsOrForeignKey?: string | Non
/**
* Stores association meta data for specified class
*/
export function addAssociation(target: any,
association: BaseAssociation) {
export function addAssociation<TCreationAttributes, TModelAttributes>(target: any,
association: BaseAssociation<TCreationAttributes, TModelAttributes>) {

let associations = getAssociations(target);

Expand All @@ -42,20 +42,20 @@ export function addAssociation(target: any,
/**
* Returns association meta data from specified class
*/
export function getAssociations(target: any): BaseAssociation[] | undefined {
export function getAssociations<TCreationAttributes, TModelAttributes>(target: any): BaseAssociation<TCreationAttributes, TModelAttributes>[] | undefined {
const associations = Reflect.getMetadata(ASSOCIATIONS_KEY, target);
if (associations) {
return [...associations];
}
}

export function setAssociations(target: any, associations: BaseAssociation[]) {
export function setAssociations<TCreationAttributes, TModelAttributes>(target: any, associations: BaseAssociation<TCreationAttributes, TModelAttributes>[]) {
Reflect.defineMetadata(ASSOCIATIONS_KEY, associations, target);
}

export function getAssociationsByRelation(target: any,
relatedClass: any): BaseAssociation[] {
const associations = getAssociations(target);
export function getAssociationsByRelation<TCreationAttributes, TModelAttributes>(target: any,
relatedClass: any): BaseAssociation<TCreationAttributes, TModelAttributes>[] {
const associations = getAssociations<TCreationAttributes, TModelAttributes>(target);
return (associations || []).filter(association => {
const _relatedClass = association.getAssociatedClass();
return (
Expand Down
10 changes: 5 additions & 5 deletions src/associations/shared/base-association.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import {UnionAssociationOptions} from './union-association-options';
import {Association} from './association';
import {ModelClassGetter} from "../../model/shared/model-class-getter";
import {Model} from "../../model/model/model";
import {ModelType} from "../../model/model/model";
import {Sequelize} from "../../sequelize/sequelize/sequelize";

export abstract class BaseAssociation {
export abstract class BaseAssociation<TCreationAttributes, TModelAttributes> {

constructor(private associatedClassGetter: ModelClassGetter,
constructor(private associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>,
protected options: UnionAssociationOptions) {
}

abstract getAssociation(): Association;
abstract getSequelizeOptions(model: typeof Model,
abstract getSequelizeOptions(model: ModelType<TCreationAttributes, TModelAttributes>,
sequelize: Sequelize): UnionAssociationOptions;

getAssociatedClass(): typeof Model {
getAssociatedClass(): ModelType<TCreationAttributes, TModelAttributes> {
return this.associatedClassGetter();
}

Expand Down
4 changes: 2 additions & 2 deletions src/associations/through/through-options.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {ThroughOptions as OriginThroughOptions} from 'sequelize';
import {ModelClassGetter} from "../../model/shared/model-class-getter";

export type ThroughOptions = {
export type ThroughOptions<TCreationAttributes, TModelAttributes> = {
[K in keyof OriginThroughOptions]: K extends 'model'
? ModelClassGetter | string
? ModelClassGetter<TCreationAttributes, TModelAttributes> | string
: OriginThroughOptions[K]
};
4 changes: 2 additions & 2 deletions src/model/model/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {AssociationCountOptions} from "./association/association-count-options";
import {AssociationActionOptions} from "./association/association-action-options";
import {AssociationCreateOptions} from "./association/association-create-options";

export type ModelType = typeof Model;
export type ModelCtor<M extends Model = Model> = (new () => M) & ModelType;
export type ModelType<TCreationAttributes, TModelAttributes> = new (values?: TCreationAttributes, options?: any) => Model<TModelAttributes, TCreationAttributes>;
export type ModelCtor<M extends Model = Model> = (new () => M) & typeof Model;

export type $GetType<T> = NonNullable<T> extends any[] ? NonNullable<T> : (NonNullable<T> | null);

Expand Down
4 changes: 2 additions & 2 deletions src/model/shared/model-class-getter.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import {Model} from "../model/model";
import {ModelType} from "../model/model";

export type ModelClassGetter = (returns?: void) => typeof Model;
export type ModelClassGetter<TCreationAttributes, TModelAttributes> = (returns?: void) => ModelType<TCreationAttributes, TModelAttributes>;
Loading