Skip to content

Commit

Permalink
Merge pull request #33 from samchon/v2.0
Browse files Browse the repository at this point in the history
Fix #32
  • Loading branch information
samchon authored May 1, 2022
2 parents f04de99 + 19e2dda commit 1c366c9
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 26 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.5",
"version": "2.0.7",
"description": "Faster JSON stringify with only one line",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion src/factories/FunctionFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export namespace FunctionFactory
const top: ts.Expression = expression.arguments[0]!
type = project.checker.getTypeAtLocation(top);
}

const app: IMetadata.IApplication | null = MetadataFactory.generate(project.checker, type);
const tuple = SchemaFactory.application(app);
const literal = ExpressionFactory.generate(tuple);
Expand Down
54 changes: 31 additions & 23 deletions src/factories/MetadataFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,18 @@ export namespace MetadataFactory
): IMetadata.IApplication | null
{
// CONSTRUCT SCHEMA WITH OBJECTS
const metadata: IMetadata | null = explore(collection, checker, type);
const metadata: IMetadata | null = explore
(
collection,
checker,
type
);
if (metadata === null)
return null;

// RETURNS WITH STORAGE
const storage: IMetadata.IStorage = collection.storage();
return { metadata, storage }
return { metadata, storage };
}

function explore
Expand Down Expand Up @@ -102,14 +107,14 @@ export namespace MetadataFactory
if (partialEscaped === true)
type = converted;

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

const node: ts.TypeNode | undefined = checker.typeToTypeNode(type, undefined, undefined);
if (!node)
return false;

const filter = (flag: ts.TypeFlags) => (type.getFlags() & flag) !== 0;
const check = (flag: ts.TypeFlags, literal: ts.TypeFlags, className: string) =>
{
Expand All @@ -121,19 +126,19 @@ export namespace MetadataFactory
return false
};

// UNKNOWN OR NULL
// UNKNOWN, NULL OR UNDEFINED
if (filter(ts.TypeFlags.Unknown) || filter(ts.TypeFlags.Never) || filter(ts.TypeFlags.Any))
return false;
else if (filter(ts.TypeFlags.Null))
return escaped ? false : schema.nullable = true;
else if (filter(ts.TypeFlags.Undefined))
else if (filter(ts.TypeFlags.Undefined) || filter(ts.TypeFlags.Void) || filter(ts.TypeFlags.VoidLike))
return escaped ? false : !(schema.required = false);

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

// WHEN ARRAY
if (ts.isArrayTypeNode(node))
{
Expand All @@ -157,7 +162,7 @@ export namespace MetadataFactory
(
collection,
checker,
checker.getTypeFromTypeNode(node.elements[0]!)
checker.getTypeFromTypeNode(node.elements[0]!),
);
if (elemSchema === null)
return false;
Expand All @@ -168,7 +173,7 @@ export namespace MetadataFactory
}

// WHEN OBJECT, MAYBE
else if (type.isClassOrInterface() || ts.isTypeLiteralNode(node) || type.isIntersection())
else if (filter(ts.TypeFlags.Object))
{
if (type.isIntersection())
{
Expand All @@ -180,7 +185,6 @@ export namespace MetadataFactory
nullable: false,
required: true,
};

if (type.types.every(t => iterate(fakeCollection, checker, fakeSchema, t)) === false)
return false;
else if (fakeSchema.atomics.size || fakeSchema.arraies.size || !fakeSchema.objects.size)
Expand All @@ -190,7 +194,7 @@ export namespace MetadataFactory
const key: string = emplace
(
collection,
checker,
checker,
type,
schema.nullable
);
Expand All @@ -206,22 +210,22 @@ export namespace MetadataFactory
(
collection: Collection,
checker: ts.TypeChecker,
type: ts.Type,
parent: ts.Type,
nullable: boolean
): string
{
// CHECK MEMORY
const [id, object] = collection.emplace(type, nullable);
const [id, object] = collection.emplace(parent, nullable);
if (object === null)
return id;

// PREPARE ASSETS
const isClass: boolean = type.isClass();
const isClass: boolean = parent.isClass();
const pred: (node: ts.Declaration) => boolean = isClass
? node => (ts.isParameter(node) || ts.isPropertyDeclaration(node))
: node => (ts.isPropertySignature(node) || ts.isTypeLiteralNode(node));

for (const prop of type.getProperties())
for (const prop of parent.getApparentProperties())
{
// CHECK NODE IS A FORMAL PROPERTY
const node: ts.PropertyDeclaration | undefined = (prop.getDeclarations() || [])[0] as ts.PropertyDeclaration | undefined;
Expand All @@ -233,18 +237,22 @@ export namespace MetadataFactory
// CHECK NOT PRIVATE OR PROTECTED MEMBER
if (isClass)
{
const kind = node.getChildren()[0]?.getChildren()[0]?.kind;
const kind: ts.SyntaxKind | undefined = node.getChildren()[0]?.getChildren()[0]?.kind;
if (kind === ts.SyntaxKind.PrivateKeyword || kind === ts.SyntaxKind.ProtectedKeyword)
continue;
}

// DETERMINE PROPERTY TYPE BY ADDITIONAL EXPLORATION
// GET EXACT TYPE
const key: string = node.name.getText();
const type: ts.Type | null = node.type
? checker.getTypeFromTypeNode(node.type)
: null;

const child = type ? explore(collection, checker, type) : null;
const type: ts.Type = checker.getTypeOfSymbolAtLocation(prop, node);

// CHILD METADATA BY ADDITIONAL EXPLORATION
const child = explore
(
collection,
checker,
type,
);
if (child && node.questionToken)
child.required = false;

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

// export function test_stringify_array_generic_alias(): void
// {
// const alias: Alias<number> = [1, 2, 3];
// const json: string = TSON.stringify(alias);
// const expected: string = JSON.stringify(alias);

// if (json !== expected)
// throw new Error("Bug on TSON.stringify(): failed to understand the generic array alias type.");
// }

// type Alias<T> = T[];
30 changes: 30 additions & 0 deletions test/features/test_stringify_object_generic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import TSON from "../../src";

export function test_stringify_object_generic_alias(): void
{
const something: ISomething<string> = {
value: "value",
child: {
child_value: "child_value",
child_next: "child_next"
},
elements: ["one", "two", "three"],
};
const json: string = TSON.stringify<ISomething<string>>(something);
const expected: string = JSON.stringify(something);

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

interface ISomething<T>
{
value: T;
child: IChild<T>;
elements: T[];
}
interface IChild<U>
{
child_value: U;
child_next: U;
}
22 changes: 22 additions & 0 deletions test/features/test_stringify_object_generic_alias.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import TSON from "../../src";
import { Primitive } from "../internal/Primitive";

export function test_stringify_object_generic_alias(): void
{
const alias: Alias = {
value: "Something"
};
const stringify: (input: Alias) => string = TSON.createStringifier<Alias>();

const json: string = stringify(alias);
const parsed: Alias = JSON.parse(json);

if (Primitive.equal_to(alias, parsed) === false)
throw new Error("Bug on TSON.createStringifier(): failed to understand the object alias type.");
}

interface ISomething<T>
{
value: T;
}
type Alias = ISomething<string>;
94 changes: 94 additions & 0 deletions test/features/test_stringify_object_generic_union.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { v4 } from "uuid";

import TSON from "../../src";

import { RandomGenerator } from "../internal/RandomGenerator";

export function test_stringify_object_generic_union(): void
{
const question: ISaleQuestion = {
id: v4(),
writer: "robot",
contents: RandomGenerator.array(() => ({
id: v4(),
title: RandomGenerator.string(),
body: RandomGenerator.string(),
files: RandomGenerator.array(() => ({
id: v4(),
name: RandomGenerator.string(),
extension: RandomGenerator.string(),
url: RandomGenerator.string(),
})),
created_at: new Date().toString()
})),
answer: null,
created_at: new Date().toString(),
hit: 0,
};

const json: string = TSON.stringify<ISaleEntireArticle>(question);
const expected: string = JSON.stringify(question);

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

type ISaleEntireArticle = ISaleQuestion | ISaleReview;
type ISaleQuestion = ISaleInquiry<ISaleQuestion.IContent>;
namespace ISaleQuestion
{
export type IContent = ISaleInquiry.IContent;
}
type ISaleReview = ISaleInquiry<ISaleReview.IContent>;
namespace ISaleReview
{
export interface IContent extends ISaleInquiry.IContent
{
score: number;
}
}

interface ISaleInquiry<Content extends ISaleInquiry.IContent>
extends ISaleArticle<Content>
{
writer: string;
answer: ISaleAnswer | null;
}
namespace ISaleInquiry
{
export type IContent = ISaleArticle.IContent;
}
type ISaleAnswer = ISaleArticle<ISaleAnswer.IContent>
namespace ISaleAnswer
{
export type IContent = ISaleArticle.IContent;
}
interface ISaleArticle<Content extends ISaleArticle.IContent>
{
id: string;
hit: number;
contents: Content[];
created_at: string;
}
namespace ISaleArticle
{
export interface IContent extends IUpdate
{
id: string;
created_at: string;
}
export interface IUpdate
{
title: string;
body: string;
files: Omit<IAttachmentFile, "id">[];
}
}

interface IAttachmentFile
{
id: string;
name: string;
extension: string | null;
url: string;
}
2 changes: 1 addition & 1 deletion test/manual.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ runner.register({
compiler: "ttypescript"
});

const program = require("./features/test_stringify_object_implicit.ts");
const program = require("./features/test_stringify_array_generic_alias.ts");
for (const value of Object.values(program))
if (typeof value === "function")
value();

0 comments on commit 1c366c9

Please sign in to comment.