Repository: typestack/class-validator (ts)

Issue:
fix: symbol are not handled as constraint arguments and make message generation raise an error
### Description

When implementing custom validators and using symbols as constraints, an error is raised when the error message is generated for a failed validation.

**Minimal code-snippet showcasing the problem**
<!-- Reports without code examples may be closed without investigating. --> 
<!-- Add your code snippet below between the backticks. -->
```ts
const mySymbol = Symbol('mySymbol');

function IsSameType(property: unknown, validationOptions?: ValidationOptions) {
  return function (object: object, propertyName: string): void {
    registerDecorator({
      target: object.constructor,
      propertyName: propertyName,
      options: validationOptions,
      constraints: [property],
      validator: {
        validate(value: any, args: ValidationArguments) {
          return typeof value ===  typeof args.constraints[0]
        },
        defaultMessage: buildMessage(
          eachPrefix => eachPrefix + '$property must be of type ' + typeof property,
          validationOptions
        ),
      },
    });
  };
}

class MyClass {
  @IsSameType(mySymbol)
  property: symbol;
}

const model = new MyClass();

// this will raise an error when generating the message
validator.validate(model)
```

### Expected behavior
No error should be raised when generating a failed validation message.

### Actual behavior
An error is raised when generating a failed validation message.

Pull request:
fix: handle symbols in constraintToString method
## Description
When implementing custom validators a constraint can be anything, but when `constraintToString` is called with a `Symbol` it throws a runtime error.

## Checklist
<!-- Replace  the [ ] with [x] to check the boxes. -->
- [x] the pull request title describes what this PR does (not a vague title like `Update index.md`)
- [x] the pull request targets the *default* branch of the repository (`develop`)
- [x] the code follows the established code style of the repository
  - `npm run prettier:check` passes
  - `npm run lint:check` passes
- [x] tests are added for the changes I made (if any source code was modified)
- [ ] documentation added or updated
- [x] I have run the project locally and verified that there are no errors

### Fixes
<!-- If there is no issue being resolved, open one before creating this pull request. -->
<!-- If the PR doesn't fully resolve the issue, replace 'fixes' with 'references'. -->
references #1793


Patch:
diff --git a/src/validation/ValidationUtils.ts b/src/validation/ValidationUtils.ts
index 6de3f21..7408031 100644
--- a/src/validation/ValidationUtils.ts
+++ b/src/validation/ValidationUtils.ts
@@ -8,6 +8,10 @@ export function constraintToString(constraint: unknown): string {
     return constraint.join(', ');
   }
 
+  if (typeof constraint === 'symbol') {
+    constraint = constraint.description;
+  }
+
   return `${constraint}`;
 }
 


Test patch:
diff --git a/test/functional/custom-decorators.spec.ts b/test/functional/custom-decorators.spec.ts
index b23a66f..39204ad 100644
--- a/test/functional/custom-decorators.spec.ts
+++ b/test/functional/custom-decorators.spec.ts
@@ -2,7 +2,7 @@ import { Validator } from '../../src/validation/Validator';
 import { ValidationArguments } from '../../src/validation/ValidationArguments';
 import { registerDecorator } from '../../src/register-decorator';
 import { ValidationOptions } from '../../src/decorator/ValidationOptions';
-import { ValidatorConstraint } from '../../src/decorator/decorators';
+import { buildMessage, ValidatorConstraint } from '../../src/decorator/decorators';
 import { ValidatorConstraintInterface } from '../../src/validation/ValidatorConstraintInterface';
 
 const validator = new Validator();
@@ -236,3 +236,41 @@ describe('decorator with separate validation constraint class', () => {
     });
   });
 });
+
+describe('decorator with symbol constraint', () => {
+  const mySymbol = Symbol('mySymbol');
+
+  function IsSameType(property: unknown, validationOptions?: ValidationOptions) {
+    return function (object: object, propertyName: string): void {
+      registerDecorator({
+        target: object.constructor,
+        propertyName: propertyName,
+        options: validationOptions,
+        constraints: [property],
+        validator: {
+          validate(value: any, args: ValidationArguments) {
+            return typeof value === typeof args.constraints[0];
+          },
+          defaultMessage: buildMessage(
+            eachPrefix => eachPrefix + '$property must be of type ' + typeof property,
+            validationOptions
+          ),
+        },
+      });
+    };
+  }
+
+  class MyClass {
+    @IsSameType(mySymbol)
+    property: symbol;
+  }
+
+  it('if property is not a symbol then it should fail', () => {
+    expect.assertions(2);
+    const model = new MyClass();
+    return validator.validate(model).then(errors => {
+      expect(errors.length).toEqual(1);
+      expect(errors[0].constraints.customValidation).toEqual('property must be of type symbol');
+    });
+  });
+});