Permalink
Browse files

feat(relations): Implement generics for relations to correctly type t…

…he type lookup methods
  • Loading branch information...
zakhenry committed Jul 18, 2016
1 parent e78267e commit 48cb903af8c98d3ae8d76e753fa2850f4fe7feaf
@@ -19,7 +19,7 @@ export interface PropertyDefinition {
export interface ModelMetadata {
storageKey?: string;
tableOptions?: TableOptions;
relations?: Map<RelationType, Map<string, Relation>>;
relations?: Map<RelationType, Map<string, Relation<any, any>>>;
storedProperties?: Map<string, PropertyDefinition>
identifierKey?: string;
timestamps?: {
@@ -2,33 +2,34 @@
* @module common
*/
/** End Typedoc Module Declaration */
import { ModelStatic } from '../model';
import { initializeRelationMap, ForeignRelationModelGetter, Relation } from './index';
import { ModelStatic, ModelConstructor, AbstractModel } from '../model';
import {
initializeRelationMap, ForeignRelationModelGetter, Relation,
ViaPropertyDefinition
} from './index';
import { RelationOptions } from 'typeorm/decorator/options/RelationOptions';
/**
* Defines the relationship between the current model and a foreign model vial the decorated key
* Defines the relationship between the current model and a foreign model via the decorated key
*
* Example:
* ```typescript
*
* @Model
* class Thumb extends AbstractModel {
*
* @BelongsTo(f => HandModel)
* @BelongsTo(f => HandModel, hand => hand.handId)
* public hand: HandModel;
*
* public handId: number;
* }
*
* ```
* Foreign model property is only required if there is no type annotation
*/
export function BelongsTo(foreignTypeGetter: ForeignRelationModelGetter, joinOptions?: RelationOptions): PropertyDecorator {
return (target: any, propertyKey: string) => {
export function BelongsTo<M extends AbstractModel, F extends AbstractModel>(foreignTypeGetter: ForeignRelationModelGetter<M, F>, viaProperty:ViaPropertyDefinition<F>, joinOptions?: RelationOptions): PropertyDecorator {
return (target: ModelConstructor<M>, propertyKey: string) => {
initializeRelationMap(target, 'belongsTo');
target.constructor.__metadata.relations.get('belongsTo')
.set(propertyKey, new Relation(target.constructor, foreignTypeGetter, joinOptions));
.set(propertyKey, new Relation(target.constructor, foreignTypeGetter, viaProperty, joinOptions));
};
}
@@ -12,9 +12,6 @@ export class HandModel extends AbstractModel {
public name: string;
@HasOne(f => ThumbModel)
left: ThumbModel;
@HasOne(f => ThumbModel)
right: ThumbModel;
thumb: ThumbModel;
}
@@ -2,31 +2,30 @@
* @module common
*/
/** End Typedoc Module Declaration */
import { ModelStatic } from '../model';
import { ModelStatic, AbstractModel, ModelConstructor } from '../model';
import { initializeRelationMap, ForeignRelationModelGetter, Relation } from './index';
import { RelationOptions } from 'typeorm/decorator/options/RelationOptions';
/**
* Defines the relationship between the current model and a foreign model vial the decorated key
* Defines the relationship between the current model and a foreign model via the decorated key
*
* Example:
* ```typescript
*
* @Model
* class Hand extends AbstractModel {
*
* @HasOne()
* @HasOne(f => ThumbModel)
* public thumb: ThumbModel;
* }
*
* ```
* Foreign model property is only required if there is no type annotation
*/
export function HasOne(foreignTypeGetter: ForeignRelationModelGetter, joinOptions?: RelationOptions): PropertyDecorator {
return (target: any, propertyKey: string) => {
export function HasOne<M extends AbstractModel, F extends AbstractModel>(foreignTypeGetter: ForeignRelationModelGetter<M, F>, joinOptions?: RelationOptions): PropertyDecorator {
return (target: ModelConstructor<M>, propertyKey: string) => {
initializeRelationMap(target, 'hasOne');
target.constructor.__metadata.relations.get('hasOne')
.set(propertyKey, new Relation(target.constructor, foreignTypeGetter, joinOptions));
.set(propertyKey, new Relation(target.constructor, foreignTypeGetter, null, joinOptions));
};
}
@@ -2,26 +2,30 @@
* @module common
*/
/** End Typedoc Module Declaration */
import { ModelStatic, ModelConstructor } from '../model';
import { ModelStatic, ModelConstructor, AbstractModel } from '../model';
import { initializeMetadata } from '../../metadata/metadata';
export type RelationType = 'hasOne' | 'hasMany' | 'belongsTo' | 'belongsToMany';
/**
* This is a crude method to two-way register the type of binding for relations. This is to overcome
* a limitation of Typescripts design-time decorators and node's module resolution.
* @see https://github.com/Microsoft/TypeScript/issues/4521
*/
export type ForeignRelationModelGetter = (thisStatic?:ModelStatic<any>) => ModelStatic<any>;
export type ForeignRelationModelGetter<T extends AbstractModel, F extends AbstractModel> = (thisStatic?: ModelStatic<T>|any) => ModelStatic<F>;
export type ViaPropertyDefinition<T> = (foreign: T) => any;
export class Relation {
export class Relation<M extends AbstractModel, F extends AbstractModel> {
constructor(public model:ModelStatic<any>, private foreignRelationModelGetter:ForeignRelationModelGetter, public databaseOptions?:any) {
constructor(public model: ModelStatic<M>,
private foreignRelationModelGetter: ForeignRelationModelGetter<M, F>,
public viaProperty: ViaPropertyDefinition<F>,
public databaseOptions?: any) {
}
public get foreign(){
public get foreign() {
return this.foreignRelationModelGetter(this.model);
}
@@ -40,7 +44,7 @@ export function initializeRelationMap(target: ModelConstructor<any>, type: Relat
target.constructor.__metadata.relations = new Map();
}
if (!target.constructor.__metadata.relations.has(type)){
if (!target.constructor.__metadata.relations.has(type)) {
target.constructor.__metadata.relations.set(type, new Map());
}
@@ -12,18 +12,7 @@ describe('Model Relations', () => {
expect(relations)
.toBeDefined();
expect(relations.get('hasOne')
.get('left').foreign)
.toEqual(ThumbModel);
});
it('registers hasOne relationship correctly when the type is not passed', () => {
const model = new HandModel();
const relations = model.getMetadata().relations;
expect(relations.get('hasOne')
.get('right').foreign)
.get('thumb').foreign)
.toEqual(ThumbModel);
});
@@ -11,9 +11,7 @@ export class ThumbModel extends AbstractModel {
public name: string;
@BelongsTo(f => HandModel)
@BelongsTo(f => HandModel, hand => hand.handId)
public hand: HandModel;
public handId: string;
}

0 comments on commit 48cb903

Please sign in to comment.