Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/functional.ts
Original file line number Diff line number Diff line change
Expand Up @@ -738,3 +738,36 @@ export function validateEqualsReturn<T extends (...args: any[]) => any>(
export function validateEqualsReturn(): never {
NoTransformConfigurationError("functional.validateEqualsReturn");
}

/* -----------------------------------------------------------
MATCH
----------------------------------------------------------- */
/**
* Pattern matching with types.
*
* Creates a pattern matching expression that validates input against TypeScript
* types and executes corresponding handlers. The function is transformed at
* compile-time to generate optimized conditional statements.
*
* @template T Union type to match against
* @template R Return type of the matching result
* @param input Value to pattern match
* @param cases Object with handler functions for different types
* @param otherwise Optional error handler for unmatched cases
* @returns Result of the matched handler or error handler
* @throws {@link TypeGuardError} if no otherwise handler and no match is found
*
* @author Jeongho Nam - https://github.com/samchon
*/
export function match<T, R>(
input: T,
cases: Record<string, (value: any) => R>,
otherwise?: (error: IValidation.IFailure) => R,
): R;

/**
* @internal
*/
export function match(): never {
NoTransformConfigurationError("functional.match");
}
4 changes: 4 additions & 0 deletions src/transformers/CallExpressionTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { FunctionalValidateFunctionProgrammer } from "../programmers/functional/
import { FunctionalValidateParametersProgrammer } from "../programmers/functional/FunctionalValidateParametersProgrammer";
import { FunctionalValidateReturnProgrammer } from "../programmers/functional/FunctionalValidateReturnProgrammer";
import { FunctionalGenericTransformer } from "./features/functional/FunctionalGenericTransformer";
import { FunctionalMatchTransformer } from "./features/functional/FunctionalMatchTransformer";

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

Expand Down Expand Up @@ -362,6 +363,9 @@ const FUNCTORS: Record<string, Record<string, () => Task>> = {
},
programmer: FunctionalValidateReturnProgrammer.write,
}),

// PATTERN MATCHING
match: () => FunctionalMatchTransformer.transform,
},
http: {
// FORM-DATA
Expand Down
102 changes: 102 additions & 0 deletions src/transformers/features/functional/FunctionalMatchTransformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import ts from "typescript";

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

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

export namespace FunctionalMatchTransformer {
export const transform = (props: ITransformProps): ts.Expression => {
// CHECK PARAMETER COUNT
if (props.expression.arguments.length < 2)
throw new TransformerError({
code: `typia.functional.match`,
message: `at least 2 arguments required: input and cases.`,
});

const input = props.expression.arguments[0]!;
const cases = props.expression.arguments[1]!;
const otherwise = props.expression.arguments[2];

// GET TYPE INFO
const inputType: ts.Type =
props.expression.typeArguments && props.expression.typeArguments[0]
? props.context.checker.getTypeFromTypeNode(
props.expression.typeArguments[0],
)
: props.context.checker.getTypeAtLocation(input);

// For now, create a simple conditional structure
// This will be expanded to generate optimized if-else chains
return createMatchExpression({
context: props.context,
modulo: props.modulo,
input,
cases,
otherwise,
inputType,
});
};

const createMatchExpression = (props: {
context: any;
modulo: any;
input: ts.Expression;
cases: ts.Expression;
otherwise: ts.Expression | undefined;
inputType: ts.Type;
}): ts.Expression => {
// Create an immediately invoked function expression (IIFE)
// This is a placeholder that will be expanded with actual matching logic
return ExpressionFactory.selfCall(
ts.factory.createBlock([
// Store input in a variable
ts.factory.createVariableStatement(
undefined,
ts.factory.createVariableDeclarationList([
ts.factory.createVariableDeclaration(
"input",
undefined,
undefined,
props.input,
),
], ts.NodeFlags.Const),
),

// Store cases in a variable
ts.factory.createVariableStatement(
undefined,
ts.factory.createVariableDeclarationList([
ts.factory.createVariableDeclaration(
"cases",
undefined,
undefined,
props.cases,
),
], ts.NodeFlags.Const),
),

// For now, return a simple implementation
// TODO: Replace with actual type-checking conditional logic
ts.factory.createReturnStatement(
props.otherwise
? ts.factory.createConditionalExpression(
ts.factory.createTrue(), // placeholder condition
undefined,
ts.factory.createStringLiteral("matched"),
undefined,
ts.factory.createCallExpression(
props.otherwise,
undefined,
[ts.factory.createObjectLiteralExpression([
ts.factory.createPropertyAssignment("success", ts.factory.createFalse()),
ts.factory.createPropertyAssignment("errors", ts.factory.createArrayLiteralExpression([])),
])],
),
)
: ts.factory.createStringLiteral("matched"),
),
], true),
);
};
}
22 changes: 22 additions & 0 deletions test-match.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import typia from "./lib";

// Test types for pattern matching
type Animal =
| { type: 'dog'; breed: string; }
| { type: 'cat'; lives: number; }
| { type: 'bird'; canFly: boolean; };

const animal: Animal = { type: 'dog', breed: 'Golden Retriever' };

// Basic test - this should compile and transform
const result = typia.functional.match(
animal,
{
dog: (dog: { type: 'dog'; breed: string; }) => `Dog of breed: ${dog.breed}`,
cat: (cat: { type: 'cat'; lives: number; }) => `Cat with ${cat.lives} lives`,
bird: (bird: { type: 'bird'; canFly: boolean; }) => `Bird that ${bird.canFly ? 'can' : 'cannot'} fly`,
},
(error) => `No match found: ${JSON.stringify(error)}`,
);

console.log('Match result:', result);