Skip to content

Commit 945345f

Browse files
IDAN SHEINBERGIDAN SHEINBERG
authored andcommitted
Add skipCircularCheck option
1 parent 85b756a commit 945345f

File tree

4 files changed

+51
-3
lines changed

4 files changed

+51
-3
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"@types/es6-shim": "^0.31.32",
3131
"@types/mocha": "^2.2.33",
3232
"@types/node": "0.0.2",
33+
"@types/sinon": "^2.2.2",
3334
"chai": "^3.4.1",
3435
"chai-as-promised": "^6.0.0",
3536
"del": "^2.2.1",

src/ClassTransformOptions.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,10 @@ export interface ClassTransformOptions {
5656
*/
5757
targetMaps?: TargetMap[];
5858

59+
60+
/**
61+
* If set to true then class transformer will not perform a circular check.
62+
* This option is useful when you know for sure that your types can't have a circular dependency. You usually use this option to boost the transform operation.
63+
*/
64+
skipCircularCheck?: boolean;
5965
}

src/TransformOperationExecutor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export class TransformOperationExecutor {
3636
const newValue = arrayType && this.transformationType === "plainToClass" ? new (arrayType as any)() : [];
3737
(value as any[]).forEach((subValue, index) => {
3838
const subSource = source ? source[index] : undefined;
39-
if (!this.isCircular(subValue, level)) {
39+
if (this.options.skipCircularCheck || !this.isCircular(subValue, level)) {
4040
const value = this.transform(subSource, subValue, targetType, undefined, subValue instanceof Map, level + 1);
4141
if (newValue instanceof Set) {
4242
newValue.add(value);
@@ -157,7 +157,7 @@ export class TransformOperationExecutor {
157157
continue;
158158
}
159159

160-
if (!this.isCircular(subValue, level)) {
160+
if (this.options.skipCircularCheck || !this.isCircular(subValue, level)) {
161161
let transformKey = this.transformationType === "plainToClass" ? newValueKey : key;
162162
let finalValue = this.transform(subSource, subValue, type, arrayType, isSubValueMap, level + 1);
163163
finalValue = this.applyCustomTransformations(finalValue, targetType, transformKey);

test/functional/circular-reference-problem.spec.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import "reflect-metadata";
2-
import {classToPlain, classToClass} from "../../src/index";
2+
import {classToPlain, classToClass, plainToClass} from "../../src/index";
33
import {defaultMetadataStorage} from "../../src/storage";
4+
import {TransformOperationExecutor} from "../../src/TransformOperationExecutor";
5+
import {assert} from "chai";
6+
import * as sinon from "sinon";
47

58
describe("circular reference problem", () => {
69

@@ -93,4 +96,42 @@ describe("circular reference problem", () => {
9396

9497
});
9598

99+
describe("skipCircularCheck option", () => {
100+
class Photo {
101+
id: number;
102+
filename: string;
103+
}
104+
105+
class User {
106+
id: number;
107+
firstName: string;
108+
photos: Photo[];
109+
}
110+
let isCircularSpy: sinon.SinonSpy;
111+
const photo1 = new Photo();
112+
photo1.id = 1;
113+
photo1.filename = "me.jpg";
114+
115+
const user = new User();
116+
user.firstName = "Umed Khudoiberdiev";
117+
user.photos = [photo1];
118+
119+
beforeEach(() => {
120+
isCircularSpy = sinon.spy(TransformOperationExecutor.prototype, "isCircular");
121+
});
122+
123+
afterEach(() => {
124+
isCircularSpy.restore();
125+
});
126+
127+
it("skipCircularCheck option is true", () => {
128+
const result = plainToClass<User, Object>(User, user, { skipCircularCheck: true});
129+
sinon.assert.notCalled(isCircularSpy);
130+
});
131+
132+
it("skipCircularCheck option is undefined", () => {
133+
const result = plainToClass<User, Object>(User, user);
134+
sinon.assert.called(isCircularSpy);
135+
});
136+
});
96137
});

0 commit comments

Comments
 (0)