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
219 changes: 219 additions & 0 deletions src/decorators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
import { NoTransformConfigurationError } from "./transformers/NoTransformConfigurationError";

import { IValidation } from "./IValidation";
import { TypeGuardError } from "./TypeGuardError";

/* ===========================================================
DECORATORS
- ASSERT
- IS
- VALIDATE
==============================================================
ASSERT
----------------------------------------------------------- */
/**
* Asserts a method with its parameters.
*
* Asserts a method, by wrapping the method and checking its parameters through
* {@link assert} function. If some parameter does not match the expected type, it
* throws an {@link TypeGuardError} or a custom error generated by the *errorFactory*
* parameter.
*
* For reference, {@link TypeGuardError.path} would be a little bit different with
* individual {@link assert} function. If the {@link TypeGuardError} occurs from
* some parameter, the path would start from `$input.parameters[number]`.
*
* This decorator is equivalent to using {@link functional.assertParameters} but
* works as a TypeScript method decorator for class methods.
*
* @param errorFactory Custom error factory. Default is `TypeGuardError`
* @returns Method decorator
* @throws A {@link TypeGuardError} or a custom error generated by *errorFactory*
*
* @author Jeongho Nam - https://github.com/samchon
*/
export function assert(
errorFactory?: undefined | ((props: TypeGuardError.IProps) => Error),
): MethodDecorator;

/**
* @internal
*/
export function assert(): never {
NoTransformConfigurationError("decorators.assert");
}

/**
* Asserts a method with strict equality of its parameters.
*
* Asserts a method, by wrapping the method and checking its parameters through
* {@link assertEquals} function. If some parameter does not match the expected type,
* it throws an {@link TypeGuardError} or a custom error generated by the *errorFactory*
* parameter.
*
* For reference, {@link TypeGuardError.path} would be a little bit different with
* individual {@link assertEquals} function. If the {@link TypeGuardError} occurs from
* some parameter, the path would start from `$input.parameters[number]`.
*
* This decorator is equivalent to using {@link functional.assertEqualsParameters} but
* works as a TypeScript method decorator for class methods.
*
* On the other hand, if you want to allow superfluous properties that are not enrolled
* to the parameter types, you can use {@link assert} decorator instead.
*
* @param errorFactory Custom error factory. Default is `TypeGuardError`
* @returns Method decorator
* @throws A {@link TypeGuardError} or a custom error generated by *errorFactory*
*
* @author Jeongho Nam - https://github.com/samchon
*/
export function assertEquals(
errorFactory?: undefined | ((props: TypeGuardError.IProps) => Error),
): MethodDecorator;

/**
* @internal
*/
export function assertEquals(): never {
NoTransformConfigurationError("decorators.assertEquals");
}

/* -----------------------------------------------------------
IS
----------------------------------------------------------- */
/**
* Tests a method's parameters.
*
* Tests a method, by wrapping the method and checking its parameters through
* {@link is} function. If some parameter does not match the expected type, it
* returns `null`. Otherwise there's no type error, it returns the result of the
* method.
*
* This decorator is equivalent to using {@link functional.isParameters} but
* works as a TypeScript method decorator for class methods.
*
* By the way, if you want is not just testing type checking, but also finding
* detailed type error reason(s), then use {@link assert} or {@link validate}
* decorators instead.
*
* On the other hand, if you don't want to allow any superfluous properties,
* utilize {@link equals} decorator instead.
*
* @returns Method decorator
*
* @author Jeongho Nam - https://github.com/samchon
*/
export function is(): MethodDecorator;

/**
* @internal
*/
export function is(): never {
NoTransformConfigurationError("decorators.is");
}

/**
* Tests a method's parameters with strict equality.
*
* Tests a method, by wrapping the method and checking its parameters through
* {@link equals} function. If some parameter does not match the expected type, it
* returns `null`. Otherwise there's no type error, it returns the result of the
* method.
*
* This decorator is equivalent to using {@link functional.equalsParameters} but
* works as a TypeScript method decorator for class methods.
*
* By the way, if you want is not just testing type checking, but also finding
* detailed type error reason(s), then use {@link assertEquals} or {@link validateEquals}
* decorators instead.
*
* On the other hand, if you want to allow superfluous properties that are not enrolled
* to the parameter types, you can use {@link is} decorator instead.
*
* @returns Method decorator
*
* @author Jeongho Nam - https://github.com/samchon
*/
export function equals(): MethodDecorator;

/**
* @internal
*/
export function equals(): never {
NoTransformConfigurationError("decorators.equals");
}

/* -----------------------------------------------------------
VALIDATE
----------------------------------------------------------- */
/**
* Validates a method's parameters.
*
* Validates a method, by wrapping the method and checking its parameters through
* {@link validate} function. If some parameter does not match the expected type, it
* returns {@link IValidation.IError} typed object. Otherwise there's no type error, it
* returns {@link IValidation.ISuccess} typed object instead.
*
* For reference, {@link IValidation.IError.path} would be a little bit different with
* individual {@link validate} function. If the {@link IValidation.IError} occurs from
* some parameter, the path would start from `$input.parameters[number]`.
*
* This decorator is equivalent to using {@link functional.validateParameters} but
* works as a TypeScript method decorator for class methods.
*
* By the way, if what you want is not finding every type errors, but just finding
* the 1st type error, then use {@link assert} decorator instead. Otherwise, if you
* just want to know whether the parameters are matched with their types, {@link is}
* decorator is the way to go.
*
* On the other hand, if you don't want to allow any superfluous properties, utilize
* {@link validateEquals} decorator instead.
*
* @returns Method decorator
*
* @author Jeongho Nam - https://github.com/samchon
*/
export function validate(): MethodDecorator;

/**
* @internal
*/
export function validate(): never {
NoTransformConfigurationError("decorators.validate");
}

/**
* Validates a method's parameters with strict equality.
*
* Validates a method, by wrapping the method and checking its parameters through
* {@link validateEquals} function. If some parameter does not match the expected type, it
* returns {@link IValidation.IError} typed object. Otherwise there's no type error, it
* returns {@link IValidation.ISuccess} typed object instead.
*
* For reference, {@link IValidation.IError.path} would be a little bit different with
* individual {@link validateEquals} function. If the {@link IValidation.IError} occurs from
* some parameter, the path would start from `$input.parameters[number]`.
*
* This decorator is equivalent to using {@link functional.validateEqualsParameters} but
* works as a TypeScript method decorator for class methods.
*
* By the way, if what you want is not finding every type errors, but just finding
* the 1st type error, then use {@link assertEquals} decorator instead. Otherwise, if you
* just want to know whether the parameters are matched with their types, {@link equals}
* decorator is the way to go.
*
* On the other hand, if you want to allow superfluous properties that are not enrolled
* to the parameter types, you can use {@link validate} decorator instead.
*
* @returns Method decorator
*
* @author Jeongho Nam - https://github.com/samchon
*/
export function validateEquals(): MethodDecorator;

/**
* @internal
*/
export function validateEquals(): never {
NoTransformConfigurationError("decorators.validateEquals");
}
1 change: 1 addition & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { IValidation } from "./IValidation";
import { Resolved } from "./Resolved";
import { TypeGuardError } from "./TypeGuardError";

export * as decorators from "./decorators";
export * as functional from "./functional";
export * as http from "./http";
export * as llm from "./llm";
Expand Down
14 changes: 14 additions & 0 deletions src/transformers/DecoratorTransformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import ts from "typescript";

import { ITypiaContext } from "./ITypiaContext";

export namespace DecoratorTransformer {
export const transform = (props: {
context: ITypiaContext;
decorator: ts.Decorator;
}): ts.Decorator | null => {
// For now, just return the original decorator
// TODO: Implement decorator transformation logic
return props.decorator;
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import ts from "typescript";

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

export namespace DecoratorGenericTransformer {
export interface IConfig {
equals: boolean;
}
export interface ISpecification {
method: string;
config: IConfig;
programmer: (p: {
context: ITypiaContext;
modulo: ts.LeftHandSideExpression;
expression: ts.Expression;
declaration: ts.MethodDeclaration;
config: IConfig;
init?: ts.Expression;
}) => ts.Expression;
}
export const transform =
(_spec: ISpecification) =>
(props: {
context: ITypiaContext;
decorator: ts.Decorator;
expression: ts.CallExpression;
}): ts.Decorator | null => {
// This is a simplified placeholder - we need to transform the method
// that this decorator is applied to. For now, return the original decorator
return props.decorator;
};
}
23 changes: 23 additions & 0 deletions test/src/features/decorators/test_decorator_basic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import typia from "typia";

interface UserQuery {
name: string;
age: number;
}

class UserService {
// Test the decorator - for now it's just a stub
@typia.decorators.assertEquals()
async findMany(query: UserQuery): Promise<UserQuery[]> {
// Implementation here
return [query];
}
}

export const test_decorator_basic = (): void => {
console.log("Decorators module is available");

// Test that we can instantiate the class with decorator
const service = new UserService();
console.log("Service created successfully with decorator");
};