joiful-ts / joiful

TypeScript Declarative Validation for Joi
239 stars 18 forks source link

"when" operator? #113

Open Ryanauger95 opened 4 years ago

Ryanauger95 commented 4 years ago

Is it possible to use the Joi "when" operator? In Joi, one can do:

Joi .string() .valid.apply(this, days) .required() .when("action", { is: "DELETE", then: Joi.string().optional(), otherwise: Joi.string().required() })

However, with jf, @(jf .string() .valid.apply(this, days) .required() .when("action", { is: "DELETE", then: jf.string().optional(), otherwise: jf.string().required() }))

I receive an error:

TypeError: jf.string(...).valid.apply(...).required(...).when is not a function

laurence-myers commented 4 years ago

Sorry for the slow response.

It looks like we haven't implemented it, sorry. I also see we haven't implemented alternatives.

dsc-leo commented 4 years ago

Hi, is there a way to perform when with the current version? Is it possible via the any.custom() function?

My use case is to require one of two fields only. For example, I want to require email when phone is undefined, and vice versa.

laurence-myers commented 4 years ago

@dsc-leo Great idea to use custom()! I tried it out, and it works. 😁 That'll be a good workaround until joiful has the when() decorator proper.

Below is the test I wrote to verify this. I wrapped the when() in a convenience function. Note that one of the properties needs to use @lazy(), otherwise there's a circular reference between properties which causes joi/hoek to error.

    it('when(), via custom()', async () => {

        function exclusiveOr<T>(schema: Joi.AnySchema, propertyName: keyof T & string) {
            return schema.when(propertyName, {
                is: Joi.required(), // Check if the "other" property is defined
                then: Joi.forbidden(), // If so, the current schema "forbids" any value except undefined
                otherwise: Joi.required(), // Otherwise, the current schema rejects undefined values
            });
        }

        class ClassToValidate {
            @string().custom(({ schema }: { schema: Joi.Schema }) => exclusiveOr<ClassToValidate>(schema, 'phone'))
            public email?: string;

            @lazy(({ joi}) => exclusiveOr<ClassToValidate>(joi.string(), 'email'))
            public phone?: string;

            constructor() {}
        }

        let instance = new ClassToValidate();
        instance.email = 'foo@bar.com';
        expect(instance).toBeValid();

        instance = new ClassToValidate();
        instance.phone = '555-5555';
        expect(instance).toBeValid();

        instance = new ClassToValidate();
        instance.email = 'foo@bar.com';
        instance.phone = '555-5555';
        expect(instance).not.toBeValid();

        instance = new ClassToValidate();
        expect(instance).not.toBeValid();
    });
sshbio commented 4 years ago

I'll create a pr for this, if you didn't start working on it.