From a96a1537aceb36cca18a6c571d6d8dbea2c8eb2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Ho=C3=A0ng=20L=E1=BB=99c?= <75782505+niskyB@users.noreply.github.com> Date: Wed, 24 Dec 2025 12:05:04 +0700 Subject: [PATCH 1/2] fix: IsObject should return false for function --- src/decorator/typechecker/IsObject.ts | 2 +- test/functional/validation-functions-and-decorators.spec.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/decorator/typechecker/IsObject.ts b/src/decorator/typechecker/IsObject.ts index 5cbd415b67..882c012313 100644 --- a/src/decorator/typechecker/IsObject.ts +++ b/src/decorator/typechecker/IsObject.ts @@ -8,7 +8,7 @@ export const IS_OBJECT = 'isObject'; * Returns false if the value is not an object. */ export function isObject(value: unknown): value is T { - return value != null && (typeof value === 'object' || typeof value === 'function') && !Array.isArray(value); + return value != null && typeof value === 'object' && !Array.isArray(value); } /** diff --git a/test/functional/validation-functions-and-decorators.spec.ts b/test/functional/validation-functions-and-decorators.spec.ts index 6e7491d20e..1ee42f56f7 100644 --- a/test/functional/validation-functions-and-decorators.spec.ts +++ b/test/functional/validation-functions-and-decorators.spec.ts @@ -3164,6 +3164,7 @@ describe('IsObject', () => { '[]', [], [{ key: 'value' }], + function () {}, ]; class MyClass { From 9ba3c4d1453ccfe3db9d7b1b20bbe14902f7ad56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Ho=C3=A0ng=20L=E1=BB=99c?= <75782505+niskyB@users.noreply.github.com> Date: Wed, 31 Dec 2025 12:54:52 +0700 Subject: [PATCH 2/2] feat: add an option to IsObject to exclude functions from being treated as objects --- README.md | 2 +- src/decorator/typechecker/IsObject.ts | 20 +++++++++++++++---- ...alidation-functions-and-decorators.spec.ts | 10 +++++++++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 886712dd76..90d9b0ed34 100644 --- a/README.md +++ b/README.md @@ -867,7 +867,7 @@ isBoolean(value); | `@IsISO8601(options?: IsISO8601Options)` | Checks if the string is a valid ISO 8601 date format. Use the option strict = true for additional checks for a valid date. | | `@IsJSON()` | Checks if the string is valid JSON. | | `@IsJWT()` | Checks if the string is valid JWT. | -| `@IsObject()` | Checks if the object is valid Object (null, functions, arrays will return false). | +| `@IsObject(options: IsObjectOptions)` | Checks if the object is valid Object (null, arrays will return false). | | `@IsNotEmptyObject()` | Checks if the object is not empty. | | `@IsLowercase()` | Checks if the string is lowercase. | | `@IsLatLong()` | Checks if the string is a valid latitude-longitude coordinate in the format lat, long. | diff --git a/src/decorator/typechecker/IsObject.ts b/src/decorator/typechecker/IsObject.ts index 882c012313..9b9387d9e0 100644 --- a/src/decorator/typechecker/IsObject.ts +++ b/src/decorator/typechecker/IsObject.ts @@ -3,24 +3,36 @@ import { buildMessage, ValidateBy } from '../common/ValidateBy'; export const IS_OBJECT = 'isObject'; +/** + * Options to be passed to IsObject decorator. + */ +export interface IsObjectOptions { + excludeFunctions?: boolean; +} + /** * Checks if the value is valid Object. * Returns false if the value is not an object. */ -export function isObject(value: unknown): value is T { - return value != null && typeof value === 'object' && !Array.isArray(value); +export function isObject(value: unknown, options: IsObjectOptions = {}): value is T { + return ( + value != null && + (typeof value === 'object' || (typeof value === 'function' && !options.excludeFunctions)) && + !Array.isArray(value) + ); } /** * Checks if the value is valid Object. * Returns false if the value is not an object. */ -export function IsObject(validationOptions?: ValidationOptions): PropertyDecorator { +export function IsObject(options: IsObjectOptions = {}, validationOptions?: ValidationOptions): PropertyDecorator { return ValidateBy( { name: IS_OBJECT, + constraints: [options], validator: { - validate: (value, args): boolean => isObject(value), + validate: (value, args): boolean => isObject(value, args?.constraints[0]), defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be an object', validationOptions), }, }, diff --git a/test/functional/validation-functions-and-decorators.spec.ts b/test/functional/validation-functions-and-decorators.spec.ts index 1ee42f56f7..bcb7dd6853 100644 --- a/test/functional/validation-functions-and-decorators.spec.ts +++ b/test/functional/validation-functions-and-decorators.spec.ts @@ -3164,7 +3164,6 @@ describe('IsObject', () => { '[]', [], [{ key: 'value' }], - function () {}, ]; class MyClass { @@ -3172,6 +3171,11 @@ describe('IsObject', () => { someProperty: object; } + class ExcludeFunctionsTestClass { + @IsObject({ excludeFunctions: true }) + someProperty: object; + } + it('should not fail if validator.validate said that its valid', () => { return checkValidValues(new MyClass(), validValues); }); @@ -3193,6 +3197,10 @@ describe('IsObject', () => { const message = 'someProperty must be an object'; return checkReturnedError(new MyClass(), invalidValues, validationType, message); }); + + it('should fail if excludeFunctions is true and the value is a function', () => { + return checkInvalidValues(new ExcludeFunctionsTestClass(), [function () {}]); + }); }); describe('IsNotEmptyObject', () => {