Skip to content

Commit

Permalink
Merge pull request #29 from samchon/v2.0
Browse files Browse the repository at this point in the history
Fix #28
  • Loading branch information
samchon authored Apr 23, 2022
2 parents abf3866 + af2aacf commit f5401a7
Show file tree
Hide file tree
Showing 14 changed files with 253 additions and 31 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "typescript-json",
"version": "2.0.3",
"version": "2.0.4",
"description": "Faster JSON stringify with only one line",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down
24 changes: 16 additions & 8 deletions src/factories/MetadataFactory.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import crypto from "crypto";
import ts from "typescript";
import { HashMap } from "tstl/container/HashMap";
import { IPointer } from "tstl/functional/IPointer";
import { Pair } from "tstl/utility/Pair";
import { Singleton } from "tstl/thread/Singleton";

import { IMetadata } from "../structures/IMetadata";
import { TypeFactory } from "./TypeFactry";
import { IPointer } from "tstl/functional/IPointer";

export namespace MetadataFactory
{
Expand Down Expand Up @@ -62,11 +62,16 @@ export namespace MetadataFactory
counter: IPointer<number>,
schema: IMetadata,
type: ts.Type,
parentEscaped: boolean = false
): boolean
{
type = TypeFactory.escape(checker, type);
const [converted, partialEscaped] = TypeFactory.escape(checker, type);
if (partialEscaped === true)
type = converted;

const escaped: boolean = partialEscaped || parentEscaped;;
if (type.isUnion())
return type.types.every(t => iterate(checker, dict, counter, schema, t));
return type.types.every(t => iterate(checker, dict, counter, schema, t, escaped));

const node: ts.TypeNode | undefined = checker.typeToTypeNode(type, undefined, undefined);
if (!node)
Expand All @@ -87,18 +92,21 @@ export namespace MetadataFactory
if (filter(ts.TypeFlags.Unknown) || filter(ts.TypeFlags.Never) || filter(ts.TypeFlags.Any))
return false;
else if (filter(ts.TypeFlags.Null))
return schema.nullable = true;
return escaped ? false : schema.nullable = true;
else if (filter(ts.TypeFlags.Undefined))
return !(schema.required = false);
return escaped ? false : !(schema.required = false);

// ATOMIC VALUE TYPES
for (const [flag, literal, className] of ATOMICS.get())
if (check(flag, literal, className) === true)
return true;
return escaped ? false : true;

// WHEN ARRAY
if (ts.isArrayTypeNode(node))
{
if (escaped)
return false;

const elemType: ts.Type | null = checker.getTypeArguments(type as ts.TypeReference)[0] || null;
const elemSchema: IMetadata | null = explore(checker, dict, counter, elemType);

Expand All @@ -109,7 +117,7 @@ export namespace MetadataFactory
// WHEN TUPLE
else if (ts.isTupleTypeNode(node))
{
if (node.elements.length === 0)
if (escaped || node.elements.length === 0)
return false;

const elemSchema: IMetadata | null = explore
Expand Down Expand Up @@ -151,7 +159,7 @@ export namespace MetadataFactory
const key: string = emplace(checker, dict, counter, type, schema.nullable);
schema.objects.add(`external#/${key}`);
}
return true;
return !escaped;
}

function emplace
Expand Down
5 changes: 3 additions & 2 deletions src/factories/TypeFactry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import ts from "typescript";

export namespace TypeFactory
{
export function escape(checker: ts.TypeChecker, type: ts.Type): ts.Type
export function escape(checker: ts.TypeChecker, type: ts.Type): [ts.Type, boolean]
{
return get_return_type(checker, type, "toJSON") ?? type;
const converted: ts.Type | null = get_return_type(checker, type, "toJSON");
return [converted || type, !!converted];
}

export function is_function(node: ts.Node): boolean
Expand Down
4 changes: 2 additions & 2 deletions test/features/test_stringify_class_public.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import TSON from "../../src";

export function test_stringify_class_public(): void
{
const accessor: Accessor = new Accessor("public", "public", "protected", "private");
const accessor: Accessor = new Accessor("implicit", "public", "protected", "private");
const json: string = TSON.stringify<Accessor>(accessor);
const expected: string = JSON.stringify({ implicit: "public", shown: "public" });
const expected: string = JSON.stringify({ implicit: "implicit", shown: "public" });

if (json !== expected)
throw new Error("Bug on TSON.stringify(): failed to understand the private class member.");
Expand Down
13 changes: 0 additions & 13 deletions test/features/test_stringify_to_json_object_closure.ts

This file was deleted.

25 changes: 25 additions & 0 deletions test/features/test_stringify_to_json_to_array..ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import TSON from "../../src";

export function test_stringify_to_json_to_array(): void
{
const something: Something = new Something(10);
const json: string = TSON.stringify<Something>(something);
const expected: string = JSON.stringify(something);

if (json !== expected)
throw new Error("Bug on TSON.stringify(): failed to undertand the Object.toJSON() returning array.");
}

class Something
{
public constructor(public readonly value: number)
{
}

public toJSON(): number[]
{
return new Array(this.value)
.fill(0)
.map((_, index) => index + 1);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import TSON from "../../src";

export function test_stringify_to_json_class(): void
export function test_stringify_to_json_to_atomic(): void
{
const operator: Operator = new Operator(2, 3, 4);
const json: string = TSON.stringify<Operator>(operator);

if (json !== "9")
throw new Error("Bug on TSON.stringify(): failed to detect the toJSON() method.");
throw new Error("Bug on TSON.stringify(): failed to understand the atomic value.");
}

class Operator
Expand Down
31 changes: 31 additions & 0 deletions test/features/test_stringify_to_json_to_atomic_nullable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import TSON from "../../src";

export function test_stringify_to_json_to_atomic_nullable(): void
{
for (const value of [1, 2])
test(value);
}

function test(value: number): void
{
const something: Something = new Something(value);
const json: string = TSON.stringify<Something>(something);
const expected: string = JSON.stringify(something);

if (json !== expected)
throw new Error("Bug on TSON.stringify(): failed to undertand the Object.toJSON() returning nullable atomic value.");
}

class Something
{
public constructor(public readonly value: number)
{
}

public toJSON(): null | number
{
return this.value % 2 === 0
? this.value
: null;
}
}
32 changes: 32 additions & 0 deletions test/features/test_stringify_to_json_to_atomic_union.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import TSON from "../../src";

export function test_stringify_to_json_to_atomic_union(): void
{
for (const value of [1, 2])
test(value);

}

function test(value: number): void
{
const something: Something = new Something(value);
const json: string = TSON.stringify<Something>(something);
const expected: string = JSON.stringify(something);

if (json !== expected)
throw new Error("Bug on TSON.stringify(): failed to undertand the Object.toJSON() returning atomic union value.");
}

class Something
{
public constructor(public readonly value: number)
{
}

public toJSON(): number | string
{
return this.value % 2 === 0
? this.value
: this.value.toString();
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import TSON from "../../src";

export function test_stringify_to_json_class_closure(): void
export function test_stringify_to_json_to_class_closure(): void
{
const operator: Operator = new Operator(2, 3, 4);
const json: string = TSON.stringify<Operator>(operator);
const expected: string = JSON.stringify(operator);

if (json !== "9")
throw new Error("Bug on TSON.stringify(): failed to detect the toJSON() closure function.");
if (json !== expected)
throw new Error("Bug on TSON.stringify(): failed to understand the Object.toJSON() closure method.");
}

class Operator
Expand Down
49 changes: 49 additions & 0 deletions test/features/test_stringify_to_json_to_complicate_union.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import TSON from "../../src";

export function test_stringify_to_json_to_complicated_union(): void
{
new Array(5)
.fill(0)
.forEach((_, index) => test(index + 1));
}

function test(value: number): void
{
const something: Something = new Something(value);
const json: string = TSON.stringify(something);
const expected: string = JSON.stringify(something);

if (json !== expected)
throw new Error("Bug on TSON.stringify(): failed to understand the Object.toJSON() returning complicate union.");
}

class Something
{
public constructor(public readonly value: number)
{
}

public toJSON(): IX | IY | number[] | string | null
{
if (this.value === 1)
return { x: 1 };
else if (this.value === 2)
return { y: "2" };
else if (this.value === 3)
return [1, 2, 3];
else if (this.value === 4)
return "4";
else
return null;
}
}

interface IX
{
x: number;
}

interface IY
{
y: string;
}
33 changes: 33 additions & 0 deletions test/features/test_stringify_to_json_to_object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import TSON from "../../src";

export function test_stringify_to_json_to_object(): void
{
const operator: Operator = new Operator(2, 3, 4);
const json: string = TSON.stringify<Operator>(operator);
const expected: string = JSON.stringify(operator);

if (json !== expected)
throw new Error("Bug on TSON.stringify(): failed to understand the Object.toJSON() returning object.");
}

class Operator
{
public constructor
(
public readonly x: number,
public readonly y: number,
public readonly z: number
)
{
}

public toJSON()
{
return {
x: this.x,
y: this.y,
z: this.z,
solution: this.x + this.y + this.z
};
}
}
14 changes: 14 additions & 0 deletions test/features/test_stringify_to_json_to_object_closure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import TSON from "../../src";

export function test_stringify_to_json_to_object_closure(): void
{
const obj = {
value: 9,
toJSON: () => 2 + 3 + 4
};
const json: string = TSON.stringify<typeof obj>(obj);
const expected: string = JSON.stringify(obj);

if (json !== expected)
throw new Error("Bug on TSON.stringify(): failed to understand the primitive toJSON() closure function.");
}
41 changes: 41 additions & 0 deletions test/features/test_stringify_to_json_to_object_union.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import TSON from "../../src";

export function test_stringify_to_json_to_object_union(): void
{
for (const value of [1, 2])
test(value);
}

function test(value: number): void
{
const something: Something = new Something(value);
const json: string = TSON.stringify<Something>(something);
const expected: string = JSON.stringify(something);

if (json !== expected)
throw new Error("Bug on TSON.stringify(): failed to undertand the Object.toJSON() returning union object.");
}

class Something
{
public constructor(public readonly value: number)
{
}

public toJSON(): IX | IY
{
return this.value % 2 === 0
? { x: this.value }
: { y: this.value.toString() };
}
}

interface IX
{
x: number;
}

interface IY
{
y: string;
}

0 comments on commit f5401a7

Please sign in to comment.