question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

feat: @IsOptional should works only for undefined values

See original GitHub issue

This code:

import { IsNotEmpty, IsOptional, IsString, validate } from 'class-validator';
import { plainToClass } from 'class-transformer';

const PATCH = 'patch';
const POST = 'post';

export class Test {
  @IsOptional({ groups: [PATCH] })
  @IsNotEmpty({ always: true })
  @IsString()
  name: string;
}

async function getValidationErrors(obj, group) {
  return await validate(plainToClass(Test, obj), { groups: [group] });
}

describe('Test', () => {
  it('should fail on post without name', async () => {
    const errors = await getValidationErrors({}, POST);
    expect(errors).not.toEqual([]);
  });
  it('should fail on post when name is undefined', async () => {
    const errors = await getValidationErrors({ name: undefined }, POST);
    expect(errors).not.toEqual([]);
  });
  it('should fail on post when name is null', async () => {
    const errors = await getValidationErrors({ name: null }, POST);
    expect(errors).not.toEqual([]);
  });
  it('should fail on post when name is empty', async () => {
    const errors = await getValidationErrors({ name: '' }, POST);
    expect(errors).not.toEqual([]);
  });
  it('should succeed on patch without name property', async () => {
    const errors = await getValidationErrors({}, PATCH);
    expect(errors).toEqual([]);
  });
  it('should fail on patch when name is undefined', async () => {
    const errors = await getValidationErrors({ name: undefined }, PATCH);
    expect(errors).not.toEqual([]);
  });
  it('should fail on patch when name is null', async () => {
    const errors = await getValidationErrors({ name: null }, PATCH);
    expect(errors).not.toEqual([]);
  });
  it('should fail on patch when name is empty', async () => {
    const errors = await getValidationErrors({ name: '' }, PATCH);
    expect(errors).not.toEqual([]);
  });
});

gives the following results: Screen Shot 2019-12-19 at 18 15 43 Am I missing something here?

Looking at the code of @IsOptional I see that it checks that the property is not undefined or null, that’s why the test for empty string is not failing, but in theory shouldn’t it check that this property exists on the object?

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:19
  • Comments:12 (5 by maintainers)

github_iconTop GitHub Comments

18reactions
sam3dcommented, May 5, 2020

@vlapo How about something like an extra config option? The implementation I’ve got in my fork doesn’t break backwards compatibility (as the option defaults to true, which is the default behaviour):

@isOptional({ nullable: false }) // object.hasOwnProperty(property);
@isOptional({ nullable: true }) // value !== undefined && value !== null
@isOptional() // value !== undefined && value !== null

The commit on the fork that adds this behaviour: https://github.com/se-internal/class-validator/commit/5ac9fa0c17ad43ecb0e8642675447b380a54139e

11reactions
rusconcommented, Sep 16, 2020
/**
 * Skips validation if the target is null
 *
 * @example
 * ```typescript
 * class TestModel {
 *     @IsNullable({ always: true })
 *     big: string | null;
 * }
 * ```
 */
export function IsNullable(options?: ValidationOptions): PropertyDecorator {
    return function IsNullableDecorator(prototype: object, propertyKey: string | symbol): void {
        ValidateIf((obj): boolean => null !== obj[propertyKey], options)(prototype, propertyKey);
    };
}
/**
 * Skips validation if the target is undefined
 *
 * @example
 * ```typescript
 * class TestModel {
 *     @IsUndefinable({ always: true })
 *     big?: string;
 * }
 * ```
 */
export function IsUndefinable(options?: ValidationOptions): PropertyDecorator {
    return function IsUndefinableDecorator(prototype: object, propertyKey: string | symbol): void {
        ValidateIf((obj): boolean => undefined !== obj[propertyKey], options)(prototype, propertyKey);
    };
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

class validator - How to allow null, but forbid undefined?
It turns out that this is possible by using conditional validation ValidateIf : class DbRow { @IsNumber() id!: number; ...
Read more >
Java - Null Handling With Optionals | by Ömer Kurular - Medium
If no value exists inside Optional object, then this method will throw NoSuchElementException. So, we can use it as follows or just use...
Read more >
How to Deal with Optional Things and "Undefined" in TypeScript
Working with JavaScript means working with undefined . It's a standard way to say, ... You must tell TypeScript if a property is...
Read more >
If nulls are evil, what should be used when a value can be ...
Lots of things are better to return than null. An empty string (""); An empty collection; An "optional" or "maybe" monad; A function...
Read more >
Is 'optional<list>' bad practice in Java? - Quora
A list containing no values is, by definition, empty. You could make a case for distinguishing an empty list from a missing list...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found