feat(class-transformer-shim): Added support for metadata reflection

closes #4219
This commit is contained in:
Hagen Schulze 2019-06-01 16:54:58 +02:00
parent 6fd579301e
commit 20f6e9cd68
8 changed files with 200 additions and 90 deletions

View File

@ -22,6 +22,7 @@ feel free to ask us and community.
* added support for `dirty_read` (NOLOCK) in SQLServer ([#4133](https://github.com/typeorm/typeorm/pull/4133))
* extend afterLoad() subscriber interface to take LoadEvent ([issue #4185](https://github.com/typeorm/typeorm/issues/4185))
* relation decorators (e.g. `@OneToMany`) now also accept `string` instead of `typeFunction`, which prevents circular dependency issues in the frontend/browser ([issue #4190](https://github.com/typeorm/typeorm/issues/4190))
* added support for metadata reflection in typeorm-class-transformer-shim.js ([issue #4219](https://github.com/typeorm/typeorm/issues/4219))
## 0.2.17 (2019-05-01)

View File

@ -1,5 +1,6 @@
// this "shim" can be used on the frontend to make class-transformer to work with typeorm models automatically
// without having to put @Type decorator on properties that already have type information inside relational decorators.
// if "reflect-metadata" is imported, you can also leave out the @Type decorator for Date or other types.
// using this shim you can share same models across backend and frontend more easily.
// to use this shim simply configure your systemjs/webpack configuration to use this file instead of typeorm module.
@ -23,225 +24,214 @@
const class_transformer_1 = require("class-transformer"); // import {Type} from "class-transformer";
/**
* If a metadata designType exists, it returns a function
* that resolves to the designType
*/
function getDesignTypeFunction(object, propertyName) {
if (
typeof Reflect !== "undefined" &&
Reflect.getMetadata instanceof Function
) {
var type = Reflect.getMetadata("design:type", object, propertyName);
if (type instanceof Function) {
return function() {
return type;
};
}
}
}
/**
* Returns a decorator that maps the property type
* to the `@Type` decorator of class-transformer
*/
function makePropertyDecorator(typeFunction) {
return function(object, propertyName) {
if (!(typeFunction instanceof Function)) {
typeFunction = getDesignTypeFunction(object, propertyName);
}
if (typeFunction) {
class_transformer_1.Type(typeFunction)(object, propertyName);
}
};
}
// columns
/* export */
function Column(typeOrOptions, options) {
return function (object, propertyName) {
if (typeOrOptions instanceof Function)
class_transformer_1.Type(typeOrOptions)(object, propertyName);
};
return makePropertyDecorator(typeOrOptions);
}
exports.Column = Column;
/* export */ function CreateDateColumn(options) {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.CreateDateColumn = CreateDateColumn;
/* export */
function ObjectIdColumn(typeOrOptions, options) {
return function (object, propertyName) {
if (typeOrOptions instanceof Function)
class_transformer_1.Type(typeOrOptions)(object, propertyName);
};
return makePropertyDecorator(typeOrOptions);
}
exports.ObjectIdColumn = ObjectIdColumn;
/* export */ function PrimaryColumn(typeOrOptions, options) {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.PrimaryColumn = PrimaryColumn;
/* export */ function PrimaryGeneratedColumn(options) {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.PrimaryGeneratedColumn = PrimaryGeneratedColumn;
/* export */ function UpdateDateColumn(options) {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.UpdateDateColumn = UpdateDateColumn;
/* export */ function VersionColumn(options) {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.VersionColumn = VersionColumn;
// listeners
/* export */ function AfterInsert() {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.AfterInsert = AfterInsert;
/* export */ function AfterLoad() {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.AfterLoad = AfterLoad;
/* export */ function AfterRemove() {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.AfterRemove = AfterRemove;
/* export */ function AfterUpdate() {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.AfterUpdate = AfterUpdate;
/* export */ function BeforeInsert() {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.BeforeInsert = BeforeInsert;
/* export */ function BeforeRemove() {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.BeforeRemove = BeforeRemove;
/* export */ function BeforeUpdate() {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.BeforeUpdate = BeforeUpdate;
/* export */ function EventSubscriber() {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.EventSubscriber = EventSubscriber;
// relations
/* export */ function JoinColumn(options) {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.JoinColumn = JoinColumn;
/* export */ function JoinTable(options) {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.JoinTable = JoinTable;
/* export */ function ManyToMany(typeFunction, inverseSideOrOptions, options) {
return function (object, propertyName) {
class_transformer_1.Type(typeFunction)(object, propertyName);
};
return makePropertyDecorator(typeFunction);
}
exports.ManyToMany = ManyToMany;
/* export */ function ManyToOne(typeFunction, inverseSideOrOptions, options) {
return function (object, propertyName) {
class_transformer_1.Type(typeFunction)(object, propertyName);
};
return makePropertyDecorator(typeFunction);
}
exports.ManyToOne = ManyToOne;
/* export */ function OneToMany(typeFunction, inverseSideOrOptions, options) {
return function (object, propertyName) {
class_transformer_1.Type(typeFunction)(object, propertyName);
};
return makePropertyDecorator(typeFunction);
}
exports.OneToMany = OneToMany;
/* export */ function OneToOne(typeFunction, inverseSideOrOptions, options) {
return function (object, propertyName) {
class_transformer_1.Type(typeFunction)(object, propertyName);
};
return makePropertyDecorator(typeFunction);
}
exports.OneToOne = OneToOne;
/* export */ function RelationCount(relation) {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.RelationCount = RelationCount;
/* export */ function RelationId(relation) {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.RelationId = RelationId;
// entities
/* export */ function ChildEntity(tableName, options) {
return function (object) {
};
return function(object) {};
}
exports.ChildEntity = ChildEntity;
/* export */ function Entity(name, options) {
return function (object) {
};
return function(object) {};
}
exports.Entity = Entity;
/* export */ function TableInheritance(type) {
return function (object) {
};
return function(object) {};
}
exports.TableInheritance = TableInheritance;
// tree
/* export */ function Tree(name, options) {
return function (object) {
};
return function(object) {};
}
exports.Tree = Tree;
/* export */ function TreeChildren(options) {
return function (object, propertyName) {
class_transformer_1.Type(typeFunction)(object, propertyName);
};
return makePropertyDecorator();
}
exports.TreeChildren = TreeChildren;
/* export */ function TreeChildrenCount(options) {
return function (object, propertyName) {
class_transformer_1.Type(typeFunction)(object, propertyName);
};
return makePropertyDecorator();
}
exports.TreeChildrenCount = TreeChildrenCount;
/* export */ function TreeLevelColumn() {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.TreeLevelColumn = TreeLevelColumn;
/* export */ function TreeParent(typeFunction) {
return function (object, propertyName) {
class_transformer_1.Type(typeFunction)(object, propertyName);
};
return makePropertyDecorator();
}
exports.TreeParent = TreeParent;
// other
/* export */ function Generated(options) {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.Generated = Generated;
/* export */ function Index() {
return function (object, propertyName) {
};
return function(object, propertyName) {};
}
exports.Index = Index;
exports.Index = Index;

47
package-lock.json generated
View File

@ -1111,6 +1111,12 @@
"integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
"dev": true
},
"class-transformer": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.2.3.tgz",
"integrity": "sha512-qsP+0xoavpOlJHuYsQJsN58HXSl8Jvveo+T37rEvCEeRfMWoytAyR0Ua/YsFgpM6AZYZ/og2PJwArwzJl1aXtQ==",
"dev": true
},
"class-utils": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
@ -2308,7 +2314,8 @@
"version": "2.1.1",
"resolved": false,
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true
"dev": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
@ -2332,13 +2339,15 @@
"version": "1.0.0",
"resolved": false,
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
"dev": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": false,
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -2348,19 +2357,22 @@
"version": "1.1.0",
"resolved": false,
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"resolved": false,
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
"dev": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"resolved": false,
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"dev": true
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@ -2481,7 +2493,8 @@
"version": "2.0.3",
"resolved": false,
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@ -2495,6 +2508,7 @@
"resolved": false,
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -2511,6 +2525,7 @@
"resolved": false,
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@ -2519,13 +2534,15 @@
"version": "0.0.8",
"resolved": false,
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
"dev": true,
"optional": true
},
"mkdirp": {
"version": "0.5.1",
"resolved": false,
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -2614,7 +2631,8 @@
"version": "1.0.1",
"resolved": false,
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"dev": true
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
@ -2628,6 +2646,7 @@
"resolved": false,
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -2723,7 +2742,8 @@
"version": "5.1.1",
"resolved": false,
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
"dev": true
"dev": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
@ -2765,6 +2785,7 @@
"resolved": false,
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@ -2786,6 +2807,7 @@
"resolved": false,
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -2843,7 +2865,8 @@
"version": "1.0.2",
"resolved": false,
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
"dev": true,
"optional": true
},
"yallist": {
"version": "3.0.3",
@ -2915,7 +2938,7 @@
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@ -6384,7 +6407,7 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
}

View File

@ -57,6 +57,7 @@
"@types/yargs": "^12.0.9",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"class-transformer": "^0.2.3",
"del": "^3.0.0",
"gulp": "^4.0.0",
"gulp-istanbul": "^1.1.3",

View File

@ -0,0 +1,14 @@
import {Shim} from "../shim";
@Shim.Entity()
export class Photo {
@Shim.PrimaryGeneratedColumn()
id: number;
@Shim.Column()
url: string;
user: any;
}

View File

@ -0,0 +1,37 @@
import {Shim} from "../shim";
import {Photo} from "./Photo";
// NOTE: The relations in here make no sense, we just care for the types.
// In real applications, this would of course not work!
@Shim.Entity()
export class User {
@Shim.PrimaryGeneratedColumn()
id: number;
@Shim.Column()
name: string;
@Shim.Column()
someDate: Date;
@Shim.OneToOne(() => Photo)
@Shim.JoinColumn()
oneToOnePhoto: Photo;
@Shim.OneToMany(() => Photo, (photo: Photo) => photo.user)
oneToManyPhotos: Photo[];
@Shim.ManyToOne(() => Photo)
@Shim.JoinColumn()
manyToOnePhoto: Photo;
@Shim.ManyToMany(() => Photo)
@Shim.JoinColumn()
manyToManyPhotos: Photo[];
@Shim.TreeParent()
treeParentPhoto: Photo;
}

View File

@ -0,0 +1,34 @@
import "reflect-metadata";
import {plainToClass} from "class-transformer";
import {Photo} from "./entity/Photo";
import {User} from "./entity/User";
describe("github issues > #4219 class-transformer-shim: support metadata reflection", () => {
it("should create instances with the correct property types", () => {
const photoLiteral = {
url: "typeorm.io"
};
const user = plainToClass(User, {
someDate: "Sat Jun 01 2019",
oneToOnePhoto: photoLiteral,
oneToManyPhotos: [photoLiteral],
manyToOnePhoto: photoLiteral,
manyToManyPhotos: [photoLiteral],
treeChildrenPhotos: [photoLiteral],
treeParentPhoto: photoLiteral
});
user.someDate.should.be.instanceof(Date);
user.oneToOnePhoto.should.be.instanceof(Photo);
user.oneToManyPhotos[0].should.be.instanceof(Photo);
user.manyToOnePhoto.should.be.instanceof(Photo);
user.manyToManyPhotos[0].should.be.instanceof(Photo);
user.treeParentPhoto.should.be.instanceof(Photo);
});
});

View File

@ -0,0 +1,10 @@
let _Shim: any;
try {
// We're in /test
_Shim = require("../../../../extra/typeorm-class-transformer-shim");
} catch (e) {
// We're in /build/compiled/test
_Shim = require("../../../../../extra/typeorm-class-transformer-shim");
}
export const Shim = _Shim;