Skip to content

Commit

Permalink
fix: typeof Model errors by using typeof Model generics (#900)
Browse files Browse the repository at this point in the history
  • Loading branch information
KapitanOczywisty authored Feb 14, 2021
1 parent 7c467d4 commit b865840
Show file tree
Hide file tree
Showing 24 changed files with 178 additions and 92 deletions.
24 changes: 12 additions & 12 deletions src/associations/belongs-to-many/belongs-to-many-association.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import {BelongsToManyOptions as OriginBelongsToManyOptions, Model, ThroughOptions} from "sequelize";

import {BaseAssociation} from '../shared/base-association';
import {BelongsToManyOptions} from './belongs-to-many-options';
import {ModelNotInitializedError} from '../../model/shared/model-not-initialized-error';
Expand All @@ -8,44 +6,46 @@ 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";
import {ThroughOptions} from "../through/through-options";

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

constructor(associatedClassGetter: ModelClassGetter,
protected options: BelongsToManyOptions) {
constructor(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>,
protected options: BelongsToManyOptions<TCreationAttributesThrough, TModelAttributesThrough>) {
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 options: BelongsToManyOptions<TCreationAttributesThrough, TModelAttributesThrough> = {...this.options};
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);

return options;
}

private getThroughOptions(sequelize: Sequelize): ThroughOptions | string {
private getThroughOptions(sequelize: Sequelize): ThroughOptions<TCreationAttributesThrough, TModelAttributesThrough> | string {
const through = this.options.through;
const throughModel = typeof through === 'object' ? through.model : through;
const throughOptions: ThroughOptions =
const throughOptions: ThroughOptions<TCreationAttributesThrough, TModelAttributesThrough> =
typeof through === 'object' ? {...through} : {} as any;

if (typeof throughModel === 'function') {
const throughModelClass = sequelize.model(throughModel());
if (!throughModelClass.isInitialized) {
throw new ModelNotInitializedError(throughModelClass, 'Association cannot be resolved.');
}
throughOptions.model = throughModelClass;
throughOptions.model = throughModelClass as any;
} else {
return throughModel;
}
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<TCreationAttributesThrough, TModelAttributesThrough> = {
[K in keyof OriginBelongsToManyOptions]: K extends 'through'
? ModelClassGetter | string | ThroughOptions
? ModelClassGetter<TCreationAttributesThrough, TModelAttributesThrough> | string | ThroughOptions<TCreationAttributesThrough, TModelAttributesThrough>
: OriginBelongsToManyOptions[K]
};
25 changes: 13 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,20 @@ 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, TCreationAttributesThrough, TModelAttributesThrough>(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>,
through: ModelClassGetter<TCreationAttributesThrough, TModelAttributesThrough> | string,
foreignKey?: string,
otherKey?: string): Function;
export function BelongsToMany<TCreationAttributes, TModelAttributes, TCreationAttributesThrough, TModelAttributesThrough>(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>,
options: BelongsToManyOptions<TCreationAttributesThrough, TModelAttributesThrough>): Function;
export function BelongsToMany<TCreationAttributes, TModelAttributes, TCreationAttributesThrough, TModelAttributesThrough>(associatedClassGetter: ModelClassGetter<TCreationAttributes, TModelAttributes>,
throughOrOptions: ModelClassGetter<TCreationAttributesThrough, TModelAttributesThrough> | string | BelongsToManyOptions<TCreationAttributesThrough, TModelAttributesThrough>,
foreignKey?: string,
otherKey?: string): Function {

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

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

addAssociation(target, new BelongsToManyAssociation(
associatedClassGetter,
options as BelongsToManyOptions,
options as BelongsToManyOptions<TCreationAttributesThrough, TModelAttributesThrough>,
)
);
};
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, TCreationAttributesThrough, TModelAttributesThrough>(relatedClass: ModelType<TCreationAttributes, TModelAttributes>,
classWithForeignKey?: ModelType<TCreationAttributesThrough, TModelAttributesThrough>,
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
Loading

0 comments on commit b865840

Please sign in to comment.