From 6b87bcc24dc16f434cab6c7af6291730c36569cc Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Fri, 7 Jul 2023 14:52:40 +0900 Subject: [PATCH] Fix #706 - support `exactOptionalPropertyTypes` option --- build/test.ts | 2 +- package.json | 2 +- packages/typescript-json/package.json | 4 +-- .../metadata/emplace_metadata_object.ts | 3 +- .../metadata/emplace_metadata_tuple.ts | 5 +-- src/metadata/Metadata.ts | 8 +++-- src/metadata/MetadataObject.ts | 2 +- src/programmers/CheckerProgrammer.ts | 8 ++--- src/programmers/IsProgrammer.ts | 2 +- src/programmers/RandomProgrammer.ts | 2 +- src/programmers/StringifyProgrammer.ts | 10 +++--- src/programmers/helpers/StringifyJoinder.ts | 2 +- .../helpers/StringifyPredicator.ts | 5 +-- src/programmers/helpers/UnionPredicator.ts | 2 +- .../internal/application_object.ts | 4 +-- .../internal/check_dynamic_properties.ts | 9 ++--- .../internal/stringify_regular_properties.ts | 8 ++--- test/tsconfig.issue.json | 3 +- website/package-lock.json | 8 ++--- website/package.json | 2 +- website/public/sitemap-0.xml | 34 +++++++++---------- 21 files changed, 64 insertions(+), 61 deletions(-) diff --git a/build/test.ts b/build/test.ts index 754fcb8f39..47498646ea 100644 --- a/build/test.ts +++ b/build/test.ts @@ -123,7 +123,7 @@ async function main(): Promise { await TestApplicationGenerator.generate(structures); // FILL SCHEMA CONTENTS - cp.execSync("npm run build:test"); + cp.execSync("npm run build:test", { stdio: "inherit" }); await TestApplicationGenerator.schema(); // GENERATE TRANSFORMED FEATURES diff --git a/package.json b/package.json index ae63ebc305..e0bce617d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "typia", - "version": "4.1.3", + "version": "4.1.4", "description": "Superfast runtime validators with only one line", "main": "lib/index.js", "typings": "lib/index.d.ts", diff --git a/packages/typescript-json/package.json b/packages/typescript-json/package.json index 053c1d759c..ebc733b09c 100644 --- a/packages/typescript-json/package.json +++ b/packages/typescript-json/package.json @@ -1,6 +1,6 @@ { "name": "typescript-json", - "version": "4.1.3", + "version": "4.1.4", "description": "Superfast runtime validators with only one line", "main": "lib/index.js", "typings": "lib/index.d.ts", @@ -68,7 +68,7 @@ }, "homepage": "https://typia.io", "dependencies": { - "typia": "4.1.3" + "typia": "4.1.4" }, "peerDependencies": { "typescript": ">= 4.7.4" diff --git a/src/factories/internal/metadata/emplace_metadata_object.ts b/src/factories/internal/metadata/emplace_metadata_object.ts index 6970e1991f..972cd2cbc3 100644 --- a/src/factories/internal/metadata/emplace_metadata_object.ts +++ b/src/factories/internal/metadata/emplace_metadata_object.ts @@ -103,8 +103,7 @@ export const emplace_metadata_object = )(type, false); // OPTIONAL, BUT CAN BE RQUIRED BY `Required` TYPE - if (node?.questionToken && (value.required === false || value.any)) - Writable(value).optional = true; + if (node?.questionToken) Writable(value).optional = true; insert(key)(value)(prop); } diff --git a/src/factories/internal/metadata/emplace_metadata_tuple.ts b/src/factories/internal/metadata/emplace_metadata_tuple.ts index 21ef4d4a6e..ba61e28de6 100644 --- a/src/factories/internal/metadata/emplace_metadata_tuple.ts +++ b/src/factories/internal/metadata/emplace_metadata_tuple.ts @@ -35,10 +35,7 @@ export const emplace_metadata_tuple = // CHECK OPTIONAL const flag: ts.ElementFlags | undefined = flagList[i]; - if ( - flag === ts.ElementFlags.Optional && - (child.required === false || child.any === true) - ) + if (flag === ts.ElementFlags.Optional) Writable(child).optional = true; // REST TYPE diff --git a/src/metadata/Metadata.ts b/src/metadata/Metadata.ts index bfeed2763c..75ddb5a976 100644 --- a/src/metadata/Metadata.ts +++ b/src/metadata/Metadata.ts @@ -306,6 +306,10 @@ export class Metadata { return this.bucket() === (this.constants.length ? 1 : 0); } + public isRequired(): boolean { + return this.required === true && this.optional === false; + } + /** * @internal */ @@ -347,7 +351,7 @@ export namespace Metadata { export const intersects = (x: Metadata, y: Metadata): boolean => { // CHECK ANY & OPTIONAL if (x.any || y.any) return true; - if (x.required === false && false === y.required) return true; + if (x.isRequired() === false && false === y.isRequired()) return true; if (x.nullable === true && true === y.nullable) return true; if (x.functional === true && y.functional === true) return true; @@ -546,7 +550,7 @@ const getName = (metadata: Metadata): string => { // OPTIONAL if (metadata.nullable === true) elements.push("null"); - if (metadata.required === false) elements.push("undefined"); + if (metadata.isRequired() === false) elements.push("undefined"); // ATOMIC for (const type of metadata.atomics) { diff --git a/src/metadata/MetadataObject.ts b/src/metadata/MetadataObject.ts index adf264f05d..90998bf712 100644 --- a/src/metadata/MetadataObject.ts +++ b/src/metadata/MetadataObject.ts @@ -81,7 +81,7 @@ export class MetadataObject { (property) => property.key.isSoleLiteral() && property.value.size() === 1 && - property.value.required === true && + property.value.isRequired() === true && property.value.nullable === false && (property.value.atomics.length === 1 || (level < 1 && diff --git a/src/programmers/CheckerProgrammer.ts b/src/programmers/CheckerProgrammer.ts index 44eea5cc27..499e170012 100644 --- a/src/programmers/CheckerProgrammer.ts +++ b/src/programmers/CheckerProgrammer.ts @@ -330,9 +330,9 @@ export namespace CheckerProgrammer { ); // UNDEFINDABLE - if (checkOptional || !meta.required) - (meta.required ? create_add(top)(input) : add)( - !meta.required, + if (checkOptional || !meta.isRequired()) + (meta.isRequired() ? create_add(top)(input) : add)( + !meta.isRequired(), ValueFactory.UNDEFINED(), ); @@ -572,7 +572,7 @@ export namespace CheckerProgrammer { obj.properties.every( (prop) => !prop.key.isSoleLiteral() || - !prop.value.required, + !prop.value.isRequired(), ), ), })(input), diff --git a/src/programmers/IsProgrammer.ts b/src/programmers/IsProgrammer.ts index bbc13ba41d..80652ae736 100644 --- a/src/programmers/IsProgrammer.ts +++ b/src/programmers/IsProgrammer.ts @@ -122,7 +122,7 @@ export namespace IsProgrammer { if ( target.size() === 1 && target.objects.length === 1 && - target.required === true && + target.isRequired() === true && target.nullable === false ) { // ONLY WHEN OBJECT WITH SOME ATOMIC PROPERTIES diff --git a/src/programmers/RandomProgrammer.ts b/src/programmers/RandomProgrammer.ts index 3b0f7ec3ab..0c2eb97838 100644 --- a/src/programmers/RandomProgrammer.ts +++ b/src/programmers/RandomProgrammer.ts @@ -225,7 +225,7 @@ export namespace RandomProgrammer { ); // NULL COALESCING - if (meta.required === false) + if (meta.isRequired() === false) expressions.push(ts.factory.createIdentifier("undefined")); if (meta.nullable === true) expressions.push(ts.factory.createNull()); diff --git a/src/programmers/StringifyProgrammer.ts b/src/programmers/StringifyProgrammer.ts index 32206240dc..2ae7a42470 100644 --- a/src/programmers/StringifyProgrammer.ts +++ b/src/programmers/StringifyProgrammer.ts @@ -158,9 +158,9 @@ export namespace StringifyProgrammer { const size: number = meta.size(); if ( size === 0 && - (meta.required === false || meta.nullable === true) + (meta.isRequired() === false || meta.nullable === true) ) { - if (meta.required === false && meta.nullable === true) + if (meta.isRequired() === false && meta.nullable === true) return explore.from === "array" ? ts.factory.createStringLiteral("null") : ts.factory.createConditionalExpression( @@ -173,7 +173,7 @@ export namespace StringifyProgrammer { undefined, ts.factory.createIdentifier("undefined"), ); - else if (meta.required === false) + else if (meta.isRequired() === false) return explore.from === "array" ? ts.factory.createStringLiteral("null") : ts.factory.createIdentifier("undefined"); @@ -420,7 +420,7 @@ export namespace StringifyProgrammer { obj.properties.every( (prop) => !prop.key.isSoleLiteral() || - !prop.value.required, + !prop.value.isRequired(), ), ), })(input), @@ -802,7 +802,7 @@ export namespace StringifyProgrammer { meta: Metadata, explore: FeatureProgrammer.IExplore, ): ((expression: ts.Expression) => ts.Expression) => { - if (meta.required === true && meta.any === false) + if (meta.isRequired() === true && meta.any === false) return (expression) => expression; return (expression) => ts.factory.createConditionalExpression( diff --git a/src/programmers/helpers/StringifyJoinder.ts b/src/programmers/helpers/StringifyJoinder.ts index 83aee3039d..4224730cba 100644 --- a/src/programmers/helpers/StringifyJoinder.ts +++ b/src/programmers/helpers/StringifyJoinder.ts @@ -41,7 +41,7 @@ export namespace StringifyJoiner { // POP LAST COMMA, IF REQUIRED const filtered: ts.Expression[] = (regular.length && - regular[regular.length - 1]!.meta.required && + regular[regular.length - 1]!.meta.isRequired() && dynamic.length === 0) || (regular.length === 0 && dynamic.length) ? expressions diff --git a/src/programmers/helpers/StringifyPredicator.ts b/src/programmers/helpers/StringifyPredicator.ts index 5e9cb884b3..d65c3b6087 100644 --- a/src/programmers/helpers/StringifyPredicator.ts +++ b/src/programmers/helpers/StringifyPredicator.ts @@ -5,8 +5,9 @@ export namespace StringifyPredicator { value.split("").some((ch) => ESCAPED.some((escaped) => escaped === ch)); export const undefindable = (meta: Metadata): boolean => - meta.required === false || - (meta.resolved !== null && meta.resolved.returns.required === false); + meta.isRequired() === false || + (meta.resolved !== null && + meta.resolved.returns.isRequired() === false); const ESCAPED = ['"', "\\", "\b", "\f", "\n", "\n", "\r", "\t"]; } diff --git a/src/programmers/helpers/UnionPredicator.ts b/src/programmers/helpers/UnionPredicator.ts index af96a1b029..c24468fc94 100644 --- a/src/programmers/helpers/UnionPredicator.ts +++ b/src/programmers/helpers/UnionPredicator.ts @@ -37,7 +37,7 @@ export namespace UnionPredicator { const children: ISpecializedProperty[] = []; obj.properties.forEach((prop) => { // MUST BE REQUIRED - if (prop.value.required === false) return; + if (prop.value.isRequired() === false) return; const key: string | null = prop.key.getSoleLiteral(); if (key === null) return; diff --git a/src/programmers/internal/application_object.ts b/src/programmers/internal/application_object.ts index 6a9266332f..ffb4b5eff8 100644 --- a/src/programmers/internal/application_object.ts +++ b/src/programmers/internal/application_object.ts @@ -45,7 +45,7 @@ export const application_object = if ( property.value.functional === true && property.value.nullable === false && - property.value.required === true && + property.value.isRequired() === true && property.value.size() === 0 ) continue; @@ -79,7 +79,7 @@ export const application_object = if (schema === null) continue; else if (key !== null) { properties[key] = schema; - if (property.value.required === true) required.push(key); + if (property.value.isRequired() === true) required.push(key); } else { const pattern: string = metadata_to_pattern(true)(property.key); if (pattern === PatternUtil.STRING) diff --git a/src/programmers/internal/check_dynamic_properties.ts b/src/programmers/internal/check_dynamic_properties.ts index e384591c46..05cb79ed37 100644 --- a/src/programmers/internal/check_dynamic_properties.ts +++ b/src/programmers/internal/check_dynamic_properties.ts @@ -30,10 +30,10 @@ export const check_dynamic_properties = const left: ts.Expression | null = props.equals === true && dynamic.length === 0 ? props.undefined === true || - regular.every((r) => r.meta.required) + regular.every((r) => r.meta.isRequired()) ? ts.factory.createStrictEquality( ts.factory.createNumericLiteral( - regular.filter((r) => r.meta.required).length, + regular.filter((r) => r.meta.isRequired()).length, ), length, ) @@ -43,7 +43,8 @@ export const check_dynamic_properties = [ length, ts.factory.createNumericLiteral( - regular.filter((r) => r.meta.required).length, + regular.filter((r) => r.meta.isRequired()) + .length, ), ts.factory.createNumericLiteral(regular.length), ], @@ -52,7 +53,7 @@ export const check_dynamic_properties = if ( props.undefined === false && left !== null && - regular.every((r) => r.meta.required) + regular.every((r) => r.meta.isRequired()) ) return left; diff --git a/src/programmers/internal/stringify_regular_properties.ts b/src/programmers/internal/stringify_regular_properties.ts index f43f7c334a..87dda2d933 100644 --- a/src/programmers/internal/stringify_regular_properties.ts +++ b/src/programmers/internal/stringify_regular_properties.ts @@ -28,7 +28,7 @@ export const stringify_regular_properties = ( base.push(ts.factory.createStringLiteral(`,`)); const empty: boolean = - (entry.meta.required === false && + (entry.meta.isRequired() === false && entry.meta.nullable === false && entry.meta.size() === 0) || (entry.meta.functional && @@ -37,7 +37,7 @@ export const stringify_regular_properties = ( if (empty === true) return; else if ( - entry.meta.required === false || + entry.meta.isRequired() === false || entry.meta.functional === true || entry.meta.any === true ) @@ -45,7 +45,7 @@ export const stringify_regular_properties = ( ts.factory.createConditionalExpression( (() => { const conditions: ts.BinaryExpression[] = []; - if (entry.meta.required === false || entry.meta.any) + if (entry.meta.isRequired() === false || entry.meta.any) conditions.push( ts.factory.createStrictEquality( ts.factory.createIdentifier("undefined"), @@ -80,4 +80,4 @@ export const stringify_regular_properties = ( * @internal */ const sequence = (meta: Metadata): number => - meta.any || !meta.required || meta.functional ? 0 : 1; + meta.any || !meta.isRequired() || meta.functional ? 0 : 1; diff --git a/test/tsconfig.issue.json b/test/tsconfig.issue.json index 212a1dd002..bfc63809bb 100644 --- a/test/tsconfig.issue.json +++ b/test/tsconfig.issue.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.json", "compilerOptions": { - "noEmit": true + "noEmit": true, + // "exactOptionalPropertyTypes": true, } } \ No newline at end of file diff --git a/website/package-lock.json b/website/package-lock.json index 660093fa02..faf563d4dc 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -21,7 +21,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "typescript": "^5.1.3", - "typia": "^4.1.3" + "typia": "^4.1.4" }, "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.1.1", @@ -6453,9 +6453,9 @@ } }, "node_modules/typia": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/typia/-/typia-4.1.3.tgz", - "integrity": "sha512-4x9atecNJfPScW7XsVVrhNMi1MtBLcpqzIj/PVu24S6CXb3RSv8QGdpTNli24SlrqG4GPivJGS2a7zGl/48HIw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/typia/-/typia-4.1.4.tgz", + "integrity": "sha512-GLMYEQuJReiFs9XsbtcE6U+vVjk2l64P7qmfNc4/3BW1ntMpDJ3Yc0azZG+HWTsRZPEXwnttzXYN5PJqflDEOA==", "dependencies": { "commander": "^10.0.0", "comment-json": "^4.2.3", diff --git a/website/package.json b/website/package.json index 4493e52b48..851459659d 100644 --- a/website/package.json +++ b/website/package.json @@ -31,7 +31,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "typescript": "^5.1.3", - "typia": "^4.1.3" + "typia": "^4.1.4" }, "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.1.1", diff --git a/website/public/sitemap-0.xml b/website/public/sitemap-0.xml index 76fe769653..2594e1f4a2 100644 --- a/website/public/sitemap-0.xml +++ b/website/public/sitemap-0.xml @@ -1,20 +1,20 @@ -https://typia.io/2023-07-04T13:02:52.708Zdaily0.7 -https://typia.io/docs/2023-07-04T13:02:52.708Zdaily0.7 -https://typia.io/docs/json/parse/2023-07-04T13:02:52.708Zdaily0.7 -https://typia.io/docs/json/schema/2023-07-04T13:02:52.708Zdaily0.7 -https://typia.io/docs/json/stringify/2023-07-04T13:02:52.708Zdaily0.7 -https://typia.io/docs/miscellaneous/2023-07-04T13:02:52.708Zdaily0.7 -https://typia.io/docs/pure/2023-07-04T13:02:52.708Zdaily0.7 -https://typia.io/docs/random/2023-07-04T13:02:52.708Zdaily0.7 -https://typia.io/docs/setup/2023-07-04T13:02:52.708Zdaily0.7 -https://typia.io/docs/utilization/nestjs/2023-07-04T13:02:52.708Zdaily0.7 -https://typia.io/docs/utilization/prisma/2023-07-04T13:02:52.708Zdaily0.7 -https://typia.io/docs/utilization/trpc/2023-07-04T13:02:52.708Zdaily0.7 -https://typia.io/docs/validators/assert/2023-07-04T13:02:52.708Zdaily0.7 -https://typia.io/docs/validators/comment-tags/2023-07-04T13:02:52.708Zdaily0.7 -https://typia.io/docs/validators/is/2023-07-04T13:02:52.708Zdaily0.7 -https://typia.io/docs/validators/validate/2023-07-04T13:02:52.708Zdaily0.7 -https://typia.io/playground/2023-07-04T13:02:52.708Zdaily0.7 +https://typia.io/2023-07-07T05:52:27.723Zdaily0.7 +https://typia.io/docs/2023-07-07T05:52:27.723Zdaily0.7 +https://typia.io/docs/json/parse/2023-07-07T05:52:27.723Zdaily0.7 +https://typia.io/docs/json/schema/2023-07-07T05:52:27.723Zdaily0.7 +https://typia.io/docs/json/stringify/2023-07-07T05:52:27.723Zdaily0.7 +https://typia.io/docs/miscellaneous/2023-07-07T05:52:27.723Zdaily0.7 +https://typia.io/docs/pure/2023-07-07T05:52:27.723Zdaily0.7 +https://typia.io/docs/random/2023-07-07T05:52:27.723Zdaily0.7 +https://typia.io/docs/setup/2023-07-07T05:52:27.723Zdaily0.7 +https://typia.io/docs/utilization/nestjs/2023-07-07T05:52:27.723Zdaily0.7 +https://typia.io/docs/utilization/prisma/2023-07-07T05:52:27.723Zdaily0.7 +https://typia.io/docs/utilization/trpc/2023-07-07T05:52:27.723Zdaily0.7 +https://typia.io/docs/validators/assert/2023-07-07T05:52:27.723Zdaily0.7 +https://typia.io/docs/validators/comment-tags/2023-07-07T05:52:27.723Zdaily0.7 +https://typia.io/docs/validators/is/2023-07-07T05:52:27.723Zdaily0.7 +https://typia.io/docs/validators/validate/2023-07-07T05:52:27.723Zdaily0.7 +https://typia.io/playground/2023-07-07T05:52:27.723Zdaily0.7 \ No newline at end of file