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 5cbd415b67..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' || typeof value === 'function') && !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 6e7491d20e..bcb7dd6853 100644 --- a/test/functional/validation-functions-and-decorators.spec.ts +++ b/test/functional/validation-functions-and-decorators.spec.ts @@ -3171,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); }); @@ -3192,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', () => {