Skip to content

Commit

Permalink
Experiment of samchon/nestia#980: ts.Type de/serialization.
Browse files Browse the repository at this point in the history
Succeeded to serialized and deserialize `ts.Type` class instnace from the JSON value level.

Also, to accomplish the experiment, refactored `typia` code never to utlize the `ts.Type` methods.
  • Loading branch information
samchon committed Aug 7, 2024
1 parent bf16c43 commit 0e4605c
Show file tree
Hide file tree
Showing 29 changed files with 170 additions and 44 deletions.
2 changes: 1 addition & 1 deletion benchmark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,6 @@
"suppress-warnings": "^1.0.2",
"tstl": "^3.0.0",
"uuid": "^9.0.1",
"typia": "../typia-6.7.2.tgz"
"typia": "../typia-6.7.3-dev.20240808.tgz"
}
}
2 changes: 1 addition & 1 deletion errors/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@
"typescript": "^5.3.2"
},
"dependencies": {
"typia": "../typia-6.7.2.tgz"
"typia": "../typia-6.7.3-dev.20240808.tgz"
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "typia",
"version": "6.7.2",
"version": "6.7.3-dev.20240808",
"description": "Superfast runtime validators with only one line",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down
4 changes: 2 additions & 2 deletions packages/typescript-json/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "typescript-json",
"version": "6.7.2",
"version": "6.7.3-dev.20240808",
"description": "Superfast runtime validators with only one line",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down Expand Up @@ -63,7 +63,7 @@
},
"homepage": "https://typia.io",
"dependencies": {
"typia": "6.7.2"
"typia": "6.7.3-dev.20240808"
},
"peerDependencies": {
"typescript": ">=4.8.0 <5.6.0"
Expand Down
80 changes: 76 additions & 4 deletions src/factories/TypeFactory.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import ts from "typescript";

import { TypePredicator } from "../utils/TypePredicator";

import { Resolved } from "../Resolved";

export namespace TypeFactory {
export const isFunction = (type: ts.Type): boolean =>
getFunction(type) !== null;
Expand All @@ -22,7 +26,10 @@ export namespace TypeFactory {
(type: ts.Type) =>
(name: string): ts.Type | null => {
// FIND TO-JSON METHOD
const symbol: ts.Symbol | undefined = type.getProperty(name);
const symbol: ts.Symbol | undefined = checker.getPropertyOfType(
type,
name,
);
if (!symbol) return null;
else if (!symbol.valueDeclaration) return null;

Expand All @@ -44,12 +51,17 @@ export namespace TypeFactory {
(checker: ts.TypeChecker) =>
(type: ts.Type, symbol?: ts.Symbol): string => {
// PRIMITIVE
symbol ??= type.aliasSymbol ?? type.getSymbol();
symbol ??= type.aliasSymbol ?? type.symbol;
if (symbol === undefined) return checker.typeToString(type);

// UNION OR INTERSECT
if (type.aliasSymbol === undefined && type.isUnionOrIntersection()) {
const joiner: string = type.isIntersection() ? " & " : " | ";
if (
type.aliasSymbol === undefined &&
TypePredicator.isUnionOrIntersection(type)
) {
const joiner: string = TypePredicator.isIntersection(type)
? " & "
: " | ";
return type.types
.map((child) => getFullName(checker)(child))
.join(joiner);
Expand Down Expand Up @@ -115,4 +127,64 @@ export namespace TypeFactory {
: ts.SyntaxKind.StringKeyword,
);
};

export const clone = (type: ts.Type): Resolved<ts.Type> =>
cloneType(type, 0, new Set());
}

const cloneType = (x: any, depth: number, visited: Set<object>): any => {
if (x === undefined || depth > 4) return undefined;
else if (typeof x === "object")
if (x === null) return null;
else if (Array.isArray(x))
return x.map((y) => cloneType(y, depth + 1, visited));
else {
visited.add(x);
const entries = (() => {
try {
return Object.entries(x);
} catch {
return undefined;
}
})();
if (entries === undefined) return undefined;
return Object.fromEntries(
entries
.filter(
([k, y]) =>
k !== "parent" &&
typeof y !== "function" &&
!(typeof y === "object" && y !== null && visited.has(y) === true),
)
.map(([k, y]) => {
// if (typeof y === "object" && y !== null) console.log(k, get_uid(y));
return [k, cloneType(y, depth + 1, visited)];
}),
);
}
return x;
};

// function get_uid(obj: object | null | undefined): number {
// // NO UID EXISTS, THEN ISSUE ONE.
// if (obj instanceof Object) {
// if (obj.hasOwnProperty("__get_m_iUID") === false) {
// const uid: number = ++__s_iUID;
// Object.defineProperty(obj, "__get_m_iUID", {
// value: function (): number {
// return uid;
// },
// });
// }

// // RETURNS
// return (obj as IObject).__get_m_iUID();
// } else if (obj === undefined) return -1;
// // is null
// else return 0;
// }

// interface IObject {
// readonly __get_m_iUID: () => number;
// }
// let __s_iUID: number = 0;
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const emplace_metadata_array_type =
// CONSTRUCT VALUE TYPE
const value: Metadata = explore_metadata(checker)(options)(collection)(
errors,
)(arrayType.getNumberIndexType()!, {
)(checker.getIndexTypeOfType(arrayType, ts.IndexKind.Number)!, {
...explore,
escaped: false,
aliased: false,
Expand Down
9 changes: 6 additions & 3 deletions src/factories/internal/metadata/emplace_metadata_object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { MetadataProperty } from "../../../schemas/metadata/MetadataProperty";
import { Writable } from "../../../typings/Writable";

import { ArrayUtil } from "../../../utils/ArrayUtil";
import { TypePredicator } from "../../../utils/TypePredicator";

import { CommentFactory } from "../../CommentFactory";
import { MetadataCollection } from "../../MetadataCollection";
Expand All @@ -28,7 +29,7 @@ export const emplace_metadata_object =
if (newbie === false) return obj;

// PREPARE ASSETS
const isClass: boolean = parent.isClass();
const isClass: boolean = TypePredicator.isClass(parent);
const pred: (node: ts.Declaration) => boolean = isClass
? (node) => {
const kind: ts.SyntaxKind | undefined = node
Expand Down Expand Up @@ -71,7 +72,9 @@ export const emplace_metadata_object =
//----
// REGULAR PROPERTIES
//----
for (const prop of parent.getApparentProperties()) {
for (const prop of checker.getPropertiesOfType(
checker.getApparentType(parent),
)) {
// CHECK INTERNAL TAG
if (
(prop.getJsDocTags(checker) ?? []).find(
Expand Down Expand Up @@ -170,7 +173,7 @@ const isProperty = (node: ts.Declaration) =>
ts.isTypeLiteralNode(node);

const iterate_optional_coalesce = (meta: Metadata, type: ts.Type): void => {
if (type.isUnionOrIntersection())
if (TypePredicator.isUnionOrIntersection(type))
type.types.forEach((child) => iterate_optional_coalesce(meta, child));
else iterate_metadata_coalesce(meta, type);
};
4 changes: 3 additions & 1 deletion src/factories/internal/metadata/iterate_metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import ts from "typescript";

import { Metadata } from "../../../schemas/metadata/Metadata";

import { TypePredicator } from "../../../utils/TypePredicator";

import { MetadataCollection } from "../../MetadataCollection";
import { MetadataFactory } from "../../MetadataFactory";
import { TypeFactory } from "../../TypeFactory";
Expand All @@ -26,7 +28,7 @@ export const iterate_metadata =
(collection: MetadataCollection) =>
(errors: MetadataFactory.IError[]) =>
(meta: Metadata, type: ts.Type, explore: MetadataFactory.IExplore): void => {
if (type.isTypeParameter() === true) {
if (TypePredicator.isTypeParameter(type) === true) {
errors.push({
name: TypeFactory.getFullName(checker)(type),
explore: { ...explore },
Expand Down
3 changes: 2 additions & 1 deletion src/factories/internal/metadata/iterate_metadata_array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { MetadataArray } from "../../../schemas/metadata/MetadataArray";
import { MetadataArrayType } from "../../../schemas/metadata/MetadataArrayType";

import { ArrayUtil } from "../../../utils/ArrayUtil";
import { TypePredicator } from "../../../utils/TypePredicator";

import { MetadataCollection } from "../../MetadataCollection";
import { MetadataFactory } from "../../MetadataFactory";
Expand Down Expand Up @@ -49,7 +50,7 @@ const find_array_extended =

memory.set(type, null);
const res: ts.Type | null = (() => {
if (type.isClassOrInterface() === false) return null;
if (TypePredicator.isInterfaceOrClass(type) === false) return null;
for (const t of type.resolvedBaseTypes ?? [])
if (checker.isArrayType(t)) return t;
else {
Expand Down
2 changes: 1 addition & 1 deletion src/factories/internal/metadata/iterate_metadata_atomic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ArrayUtil } from "../../../utils/ArrayUtil";

const same = (type: ts.Type | null) => {
if (type === null) return () => false;
return (flag: ts.TypeFlags) => (type.getFlags() & flag) !== 0;
return (flag: ts.TypeFlags) => (type.flags & flag) !== 0;
};

export const iterate_metadata_atomic = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const iterate_metadata_coalesce = (
meta: Metadata,
type: ts.Type,
): boolean => {
const filter = (flag: ts.TypeFlags) => (type.getFlags() & flag) !== 0;
const filter = (flag: ts.TypeFlags) => (type.flags & flag) !== 0;
if (filter(ts.TypeFlags.Unknown) || filter(ts.TypeFlags.Any)) {
Writable(meta).any = true;
return true;
Expand Down
5 changes: 3 additions & 2 deletions src/factories/internal/metadata/iterate_metadata_constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { MetadataConstant } from "../../../schemas/metadata/MetadataConstant";
import { MetadataConstantValue } from "../../../schemas/metadata/MetadataConstantValue";

import { ArrayUtil } from "../../../utils/ArrayUtil";
import { TypePredicator } from "../../../utils/TypePredicator";

import { CommentFactory } from "../../CommentFactory";
import { MetadataFactory } from "../../MetadataFactory";
Expand All @@ -15,7 +16,7 @@ export const iterate_metadata_constant =
(meta: Metadata, type: ts.Type): boolean => {
if (!options.constant) return false;

const filter = (flag: ts.TypeFlags) => (type.getFlags() & flag) !== 0;
const filter = (flag: ts.TypeFlags) => (type.flags & flag) !== 0;
const comment = () => {
if (!filter(ts.TypeFlags.EnumLiteral)) return {};
return {
Expand All @@ -25,7 +26,7 @@ export const iterate_metadata_constant =
: undefined,
};
};
if (type.isLiteral()) {
if (TypePredicator.isLiteral(type)) {
const value: string | number | bigint =
typeof type.value === "object"
? BigInt(`${type.value.negative ? "-" : ""}${type.value.base10Value}`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { MetadataConstantValue } from "../../../schemas/metadata/MetadataConstan
import { MetadataTemplate } from "../../../schemas/metadata/MetadataTemplate";

import { ArrayUtil } from "../../../utils/ArrayUtil";
import { TypePredicator } from "../../../utils/TypePredicator";

import { MetadataCollection } from "../../MetadataCollection";
import { MetadataFactory } from "../../MetadataFactory";
Expand All @@ -26,12 +27,12 @@ export const iterate_metadata_intersection =
type: ts.Type,
explore: MetadataFactory.IExplore,
): boolean => {
if (!type.isIntersection()) return false;
if (TypePredicator.isIntersection(type) === false) return false;
if (
// ONLY OBJECT TYPED INTERSECTION
type.types.every(
(child) =>
(child.getFlags() & ts.TypeFlags.Object) !== 0 &&
(child.flags & ts.TypeFlags.Object) !== 0 &&
!checker.isArrayType(child) &&
!checker.isTupleType(child),
)
Expand Down
2 changes: 1 addition & 1 deletion src/factories/internal/metadata/iterate_metadata_map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const iterate_metadata_map =
): boolean => {
type = checker.getApparentType(type);

const name = TypeFactory.getFullName(checker)(type, type.getSymbol());
const name = TypeFactory.getFullName(checker)(type, type.symbol);
const generic = type.aliasSymbol
? type.aliasTypeArguments
: checker.getTypeArguments(type as ts.TypeReference);
Expand Down
5 changes: 1 addition & 4 deletions src/factories/internal/metadata/iterate_metadata_native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@ export const iterate_metadata_native =
(checker: ts.TypeChecker) =>
(meta: Metadata, type: ts.Type): boolean => {
const validator = validate(checker)(type);
const name: string = TypeFactory.getFullName(checker)(
type,
type.getSymbol(),
);
const name: string = TypeFactory.getFullName(checker)(type, type.symbol);

const simple = SIMPLES.get(name);
if (simple && validator(simple)) {
Expand Down
5 changes: 3 additions & 2 deletions src/factories/internal/metadata/iterate_metadata_object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Metadata } from "../../../schemas/metadata/Metadata";
import { MetadataObject } from "../../../schemas/metadata/MetadataObject";

import { ArrayUtil } from "../../../utils/ArrayUtil";
import { TypePredicator } from "../../../utils/TypePredicator";

import { MetadataCollection } from "../../MetadataCollection";
import { MetadataFactory } from "../../MetadataFactory";
Expand All @@ -16,10 +17,10 @@ export const iterate_metadata_object =
(errors: MetadataFactory.IError[]) =>
(meta: Metadata, type: ts.Type, ensure: boolean = false): boolean => {
if (ensure === false) {
const filter = (flag: ts.TypeFlags) => (type.getFlags() & flag) !== 0;
const filter = (flag: ts.TypeFlags) => (type.flags & flag) !== 0;
if (
!filter(ts.TypeFlags.Object) &&
!type.isIntersection() &&
!TypePredicator.isIntersection(type) &&
(type as any).intrinsicName !== "object"
)
return false;
Expand Down
2 changes: 1 addition & 1 deletion src/factories/internal/metadata/iterate_metadata_set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const iterate_metadata_set =
): boolean => {
type = checker.getApparentType(type);

const name = TypeFactory.getFullName(checker)(type, type.getSymbol());
const name = TypeFactory.getFullName(checker)(type, type.symbol);
const generic = type.aliasSymbol
? type.aliasTypeArguments
: checker.getTypeArguments(type as ts.TypeReference);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const iterate_metadata_template =
type: ts.Type,
explore: MetadataFactory.IExplore,
): boolean => {
const filter = (flag: ts.TypeFlags) => (type.getFlags() & flag) !== 0;
const filter = (flag: ts.TypeFlags) => (type.flags & flag) !== 0;
if (!filter(ts.TypeFlags.TemplateLiteral)) return false;

const template: ts.TemplateLiteralType = type as ts.TemplateLiteralType;
Expand Down
4 changes: 3 additions & 1 deletion src/factories/internal/metadata/iterate_metadata_union.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import ts from "typescript";

import { Metadata } from "../../../schemas/metadata/Metadata";

import { TypePredicator } from "../../../utils/TypePredicator";

import { MetadataCollection } from "../../MetadataCollection";
import { MetadataFactory } from "../../MetadataFactory";
import { iterate_metadata } from "./iterate_metadata";
Expand All @@ -16,7 +18,7 @@ export const iterate_metadata_union =
type: ts.Type,
explore: MetadataFactory.IExplore,
): boolean => {
if (!type.isUnion()) return false;
if (TypePredicator.isUnion(type) === false) return false;
type.types.forEach((t) =>
iterate_metadata(checker)(options)(collection)(errors)(meta, t, {
...explore,
Expand Down
4 changes: 3 additions & 1 deletion src/transformers/features/CreateRandomTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import ts from "typescript";

import { RandomProgrammer } from "../../programmers/RandomProgrammer";

import { TypePredicator } from "../../utils/TypePredicator";

import { IProject } from "../IProject";
import { TransformerError } from "../TransformerError";

Expand All @@ -21,7 +23,7 @@ export namespace CreateRandomTransformer {
const node: ts.TypeNode = expression.typeArguments[0];
const type: ts.Type = project.checker.getTypeFromTypeNode(node);

if (type.isTypeParameter())
if (TypePredicator.isTypeParameter(type))
throw new TransformerError({
code: "typia.createRandom",
message: "non-specified generic argument.",
Expand Down
Loading

0 comments on commit 0e4605c

Please sign in to comment.