I want to use validateSync in object constructor, but I'm not able to use it with inheritance.
I've got something like this:
import { IsNotEmpty, IsString, validateSync, validate } from 'class-validator';
class Animal {
@IsNotEmpty()
@IsString()
public name: string;
constructor(name: string) {
this.name = name;
this.validate() // this method calls validate of class Dog, since this is an instance of Dog
}
protected validate() {
const errors = validateSync(this);
if (errors.length > 0) {
console.log("Animal validation error: ", errors)
}
}
}
class Dog extends Animal {
@IsNotEmpty()
@IsString()
public breed: string;
constructor(name: string, breed: string) {
super(name);
this.breed = breed
this.validate()
}
protected validate() {
const errors = validateSync(this);
if (errors.length > 0) {
console.log("Dog validation error: ", errors)
}
}
}
const dog = new Dog('Aaron', 'Golden Retriever')
The result is:
Dog validation error: [ ValidationError {
target: Dog { name: 'Aaron' },
value: undefined,
property: 'breed',
children: [],
constraints:
{ isString: 'breed must be a string',
isNotEmpty: 'breed should not be empty' } } ]
When I call Dog constructor, the code runs super() and then this.validate() of the Animal class. This method validates an instance of Dog and since Animal doesn't have breed attribute, the code throw the error above.
I don't know how to solve this issue and maybe put validation inside constructors is not a good idea.
Is there a workaround or a better way to do that?
I want to use validateSync in object constructor, but I'm not able to use it with inheritance.
I've got something like this:
import { IsNotEmpty, IsString, validateSync, validate } from 'class-validator';
class Animal {
@IsNotEmpty()
@IsString()
public name: string;
constructor(name: string) {
this.name = name;
this.validate() // this method calls validate of class Dog, since this is an instance of Dog
}
protected validate() {
const errors = validateSync(this);
if (errors.length > 0) {
console.log("Animal validation error: ", errors)
}
}
}
class Dog extends Animal {
@IsNotEmpty()
@IsString()
public breed: string;
constructor(name: string, breed: string) {
super(name);
this.breed = breed
this.validate()
}
protected validate() {
const errors = validateSync(this);
if (errors.length > 0) {
console.log("Dog validation error: ", errors)
}
}
}
const dog = new Dog('Aaron', 'Golden Retriever')
The result is:
Dog validation error: [ ValidationError {
target: Dog { name: 'Aaron' },
value: undefined,
property: 'breed',
children: [],
constraints:
{ isString: 'breed must be a string',
isNotEmpty: 'breed should not be empty' } } ]
When I call Dog constructor, the code runs super() and then this.validate() of the Animal class. This method validates an instance of Dog and since Animal doesn't have breed attribute, the code throw the error above.
I don't know how to solve this issue and maybe put validation inside constructors is not a good idea.
Is there a workaround or a better way to do that?
Share Improve this question asked Dec 24, 2018 at 12:16 nik2onik2o 932 silver badges11 bronze badges1 Answer
Reset to default 6The reason you get the error is not because Animal
doesn't have the breed
attribute (in fact it does, because the instance is actually Dog
), but because you call the validation (one of them) before setting all the values you are validating. Another thing is, you do the validation twice which doesn't sound right.
class Dog extends Animal {
constructor(name: string, breed: string) {
super(name); // <-- validation gets called in the parent but you did't set the breed property yet
this.breed = breed;
this.validate(); // <-- validation gets called for the second time, but this time it passes
}
}
And since super()
must be the first statement in the constructor, you have no way of avoiding this with your current pattern.
One of the solutions would be to avoid calling validate()
in the base class, let child classes set all the fields properly and only then do the validation.
I don't see anything wrong in validating required properties in constructor because you're preventing an object with invalid state being created. However, if it's not absolutely necessary to have all the values when the object is created, I'd suggest allowing caller to decide when to do the validation:
const dog = new Dog('Aaron');
// do some stuff like determine the breed...
dog.breed = 'Golden Retriever';
// do some more stuff
const validationResult = dog.validate(); // did I succeed?
One other note, you don't seem to have the need of having the same exact method validate()
declared in both parent and child classes.