Skip to content

Commit

Permalink
Fix typia.functional module for async or implicit return type case.
Browse files Browse the repository at this point in the history
When user inputs implicit return typed function into the `typia.functional` module, `typia.functional` could not validate exact return type value.

Also, if the input parameter function is a typeof async, it could not be validated, either.

This PR fixes such bugs.
  • Loading branch information
samchon committed Mar 6, 2024
1 parent b6aa134 commit 823c52a
Show file tree
Hide file tree
Showing 20 changed files with 168 additions and 186 deletions.
2 changes: 1 addition & 1 deletion benchmark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,6 @@
"suppress-warnings": "^1.0.2",
"tstl": "^2.5.13",
"uuid": "^9.0.1",
"typia": "D:\\github\\samchon\\typia\\typia-5.5.0.tgz"
"typia": "D:\\github\\samchon\\typia\\typia-5.5.1.tgz"
}
}
7 changes: 4 additions & 3 deletions debug/features/functional.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import fs from "fs";
import typia from "typia";

type Something = Promise<number>;
const plus = async (x: number, y: number): Something => x + y;

try {
fs.mkdirSync(__dirname + "/../bin");
} catch {}
const func = typia.functional.validateFunction(
(x: number, y: number): number => x + y,
);
const func = typia.functional.isReturn(plus);
fs.writeFileSync(`${__dirname}/../bin/functional.js`, func.toString(), "utf8");

console.log(func(3, 4));
2 changes: 1 addition & 1 deletion debug/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
"typescript": "^5.3.2"
},
"dependencies": {
"typia": "D:\\github\\samchon\\typia\\typia-5.5.0-dev.20240305.tgz"
"typia": "D:\\github\\samchon\\typia\\typia-5.5.1-dev.20240307-2.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": "D:\\github\\samchon\\typia\\typia-5.5.0.tgz"
"typia": "D:\\github\\samchon\\typia\\typia-5.5.1.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": "5.5.0",
"version": "5.5.1",
"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": "5.5.0",
"version": "5.5.1",
"description": "Superfast runtime validators with only one line",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down Expand Up @@ -62,7 +62,7 @@
},
"homepage": "https://typia.io",
"dependencies": {
"typia": "5.5.0"
"typia": "5.5.1"
},
"peerDependencies": {
"typescript": ">=4.8.0 <5.5.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,7 @@ export namespace FunctionalAssertFunctionProgrammer {
const wrapper = errorFactoryWrapper(modulo)(declaration.parameters)(init);
const { async, returns } = FunctionAssertReturnProgrammer.returnStatement(
project,
)(modulo)(equals)(
expression,
declaration.type,
declaration.parameters.map((p) =>
ts.factory.createIdentifier(p.name.getText()),
),
wrapper.name,
);
)(modulo)(equals)(expression, declaration, wrapper.name);
return ts.factory.createArrowFunction(
async
? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { IProject } from "../../transformers/IProject";

import { AssertProgrammer } from "../AssertProgrammer";
import { FunctionalAssertFunctionProgrammer } from "./FunctionalAssertFunctionProgrammer";
import { FunctionalGeneralProgrammer } from "./internal/FunctionalGeneralProgrammer";

export namespace FunctionalAssertParametersProgrammer {
export const write =
Expand All @@ -20,13 +21,9 @@ export namespace FunctionalAssertParametersProgrammer {
const wrapper = FunctionalAssertFunctionProgrammer.errorFactoryWrapper(
modulo,
)(declaration.parameters)(init);
const async: boolean = (() => {
if (declaration.type === undefined) return false;
const type: ts.Type = project.checker.getTypeFromTypeNode(
declaration.type,
);
return type.isTypeParameter() && type.symbol.name === "Promise";
})();
const { async } = FunctionalGeneralProgrammer.getReturnType(
project.checker,
)(declaration);
return ts.factory.createArrowFunction(
async
? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)]
Expand Down
30 changes: 9 additions & 21 deletions src/programmers/functional/FunctionalAssertReturnProgrammer.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import ts from "typescript";

import { TypeFactory } from "../../factories/TypeFactory";

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

import { AssertProgrammer } from "../AssertProgrammer";
import { FunctionalAssertFunctionProgrammer } from "./FunctionalAssertFunctionProgrammer";
import { FunctionalGeneralProgrammer } from "./internal/FunctionalGeneralProgrammer";

export namespace FunctionAssertReturnProgrammer {
export const write =
Expand All @@ -22,14 +21,7 @@ export namespace FunctionAssertReturnProgrammer {
)(declaration.parameters)(init);
const { async, returns: statement } = returnStatement(project)(modulo)(
equals,
)(
expression,
declaration.type,
declaration.parameters.map((p) =>
ts.factory.createIdentifier(p.name.getText()),
),
wrapper.name,
);
)(expression, declaration, wrapper.name);
return ts.factory.createArrowFunction(
async
? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)]
Expand All @@ -48,25 +40,21 @@ export namespace FunctionAssertReturnProgrammer {
(equals: boolean) =>
(
expression: ts.Expression,
typeNode: ts.TypeNode | undefined,
argumentExpressions: ts.Expression[],
declaration: ts.FunctionDeclaration,
wrapper: string,
): {
async: boolean;
returns: ts.ReturnStatement;
} => {
const [type, async]: [ts.Type, boolean] = (() => {
const type: ts.Type = project.checker.getTypeFromTypeNode(
typeNode ?? TypeFactory.keyword("any"),
);
return type.isTypeParameter() && type.symbol.name === "Promise"
? [type.aliasTypeArguments![0]!, true]
: [type, false];
})();
const { type, async } = FunctionalGeneralProgrammer.getReturnType(
project.checker,
)(declaration);
const caller: ts.CallExpression = ts.factory.createCallExpression(
expression,
undefined,
argumentExpressions,
declaration.parameters.map((p) =>
ts.factory.createIdentifier(p.name.getText()),
),
);
return {
async,
Expand Down
11 changes: 6 additions & 5 deletions src/programmers/functional/FunctionalIsFunctionProgrammer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ export namespace FunctionalIsFunctionProgrammer {
: undefined,
undefined,
declaration.parameters,
ts.factory.createUnionTypeNode([
declaration.type ??
ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
ts.factory.createTypeReferenceNode("null"),
]),
declaration.type
? ts.factory.createUnionTypeNode([
declaration.type,
ts.factory.createTypeReferenceNode("null"),
])
: undefined,
undefined,
ts.factory.createBlock(
[
Expand Down
21 changes: 10 additions & 11 deletions src/programmers/functional/FunctionalIsParametersProgrammer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { TypeFactory } from "../../factories/TypeFactory";
import { IProject } from "../../transformers/IProject";

import { IsProgrammer } from "../IsProgrammer";
import { FunctionalGeneralProgrammer } from "./internal/FunctionalGeneralProgrammer";

export namespace FunctionalIsParametersProgrammer {
export const write =
Expand All @@ -15,23 +16,21 @@ export namespace FunctionalIsParametersProgrammer {
expression: ts.Expression,
declaration: ts.FunctionDeclaration,
): ts.ArrowFunction => {
const async: boolean = (() => {
if (declaration.type === undefined) return false;
const type: ts.Type = project.checker.getTypeFromTypeNode(
declaration.type,
);
return type.isTypeParameter() && type.symbol.name === "Promise";
})();
const { async } = FunctionalGeneralProgrammer.getReturnType(
project.checker,
)(declaration);
return ts.factory.createArrowFunction(
async
? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)]
: undefined,
undefined,
declaration.parameters,
ts.factory.createUnionTypeNode([
declaration.type ?? TypeFactory.keyword("any"),
ts.factory.createTypeReferenceNode("null"),
]),
declaration.type
? ts.factory.createUnionTypeNode([
declaration.type,
ts.factory.createTypeReferenceNode("null"),
])
: undefined,
undefined,
ts.factory.createBlock(
[
Expand Down
23 changes: 10 additions & 13 deletions src/programmers/functional/FunctionalIsReturnProgrammer.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import ts from "typescript";

import { StatementFactory } from "../../factories/StatementFactory";
import { TypeFactory } from "../../factories/TypeFactory";

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

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

import { IsProgrammer } from "../IsProgrammer";
import { FunctionalGeneralProgrammer } from "./internal/FunctionalGeneralProgrammer";

export namespace FunctionalIsReturnProgrammer {
export const write =
Expand All @@ -28,10 +28,12 @@ export namespace FunctionalIsReturnProgrammer {
: undefined,
undefined,
declaration.parameters,
ts.factory.createUnionTypeNode([
declaration.type ?? TypeFactory.keyword("any"),
ts.factory.createTypeReferenceNode("null"),
]),
declaration.type
? ts.factory.createUnionTypeNode([
declaration.type,
ts.factory.createTypeReferenceNode("null"),
])
: undefined,
undefined,
ts.factory.createBlock(statements, true),
);
Expand All @@ -48,14 +50,9 @@ export namespace FunctionalIsReturnProgrammer {
async: boolean;
statements: ts.Statement[];
} => {
const [type, async]: [ts.Type, boolean] = (() => {
const type: ts.Type = project.checker.getTypeFromTypeNode(
declaration.type ?? TypeFactory.keyword("any"),
);
return type.isTypeParameter() && type.symbol.name === "Promise"
? [type.aliasTypeArguments![0]!, true]
: [type, false];
})();
const { type, async } = FunctionalGeneralProgrammer.getReturnType(
project.checker,
)(declaration);
const caller: ts.CallExpression = ts.factory.createCallExpression(
expression,
undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { StringUtil } from "../../utils/StringUtil";

import { ValidateProgrammer } from "../ValidateProgrammer";
import { FunctionalValidateFunctionProgrammer } from "./FunctionalValidateFunctionProgrammer";
import { FunctionalGeneralProgrammer } from "./internal/FunctionalGeneralProgrammer";

export namespace FunctionalValidateParametersProgrammer {
export const write =
Expand All @@ -20,13 +21,9 @@ export namespace FunctionalValidateParametersProgrammer {
expression: ts.Expression,
declaration: ts.FunctionDeclaration,
): ts.ArrowFunction => {
const async: boolean = (() => {
if (declaration.type === undefined) return false;
const type: ts.Type = project.checker.getTypeFromTypeNode(
declaration.type,
);
return type.isTypeParameter() && type.symbol.name === "Promise";
})();
const { async } = FunctionalGeneralProgrammer.getReturnType(
project.checker,
)(declaration);
const caller: ts.CallExpression = ts.factory.createCallExpression(
expression,
undefined,
Expand Down
13 changes: 4 additions & 9 deletions src/programmers/functional/FunctionalValidateReturnProgrammer.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import ts from "typescript";

import { StatementFactory } from "../../factories/StatementFactory";
import { TypeFactory } from "../../factories/TypeFactory";

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

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

import { ValidateProgrammer } from "../ValidateProgrammer";
import { FunctionalValidateFunctionProgrammer } from "./FunctionalValidateFunctionProgrammer";
import { FunctionalGeneralProgrammer } from "./internal/FunctionalGeneralProgrammer";

export namespace FunctionalValidateReturnProgrammer {
export const write =
Expand Down Expand Up @@ -55,14 +55,9 @@ export namespace FunctionalValidateReturnProgrammer {
async: boolean;
statements: ts.Statement[];
} => {
const [type, async]: [ts.Type, boolean] = (() => {
const type: ts.Type = project.checker.getTypeFromTypeNode(
declaration.type ?? TypeFactory.keyword("any"),
);
return type.isTypeParameter() && type.symbol.name === "Promise"
? [type.aliasTypeArguments![0]!, true]
: [type, false];
})();
const { type, async } = FunctionalGeneralProgrammer.getReturnType(
project.checker,
)(declaration);
const caller: ts.CallExpression = ts.factory.createCallExpression(
expression,
undefined,
Expand Down
32 changes: 32 additions & 0 deletions src/programmers/functional/internal/FunctionalGeneralProgrammer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import ts from "typescript";

import { TypeFactory } from "../../../factories/TypeFactory";

export namespace FunctionalGeneralProgrammer {
export interface IOutput {
type: ts.Type;
async: boolean;
}
export const getReturnType =
(checker: ts.TypeChecker) =>
(declaration: ts.FunctionDeclaration): IOutput => {
const signature: ts.Signature | undefined =
checker.getSignatureFromDeclaration(declaration);
const type: ts.Type =
signature?.getReturnType() ??
checker.getTypeFromTypeNode(TypeFactory.keyword("any"));

if (type.symbol?.name === "Promise") {
const generic: readonly ts.Type[] = checker.getTypeArguments(
type as ts.TypeReference,
);
return generic.length === 1
? { type: generic[0]!, async: true }
: {
type: checker.getTypeFromTypeNode(TypeFactory.keyword("any")),
async: false,
};
}
return { type, async: false };
};
}
2 changes: 1 addition & 1 deletion test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,6 @@
"suppress-warnings": "^1.0.2",
"tstl": "^2.5.13",
"uuid": "^9.0.1",
"typia": "D:\\github\\samchon\\typia\\typia-5.5.0.tgz"
"typia": "D:\\github\\samchon\\typia\\typia-5.5.1.tgz"
}
}
Loading

0 comments on commit 823c52a

Please sign in to comment.