如何允许null,但禁止undefined?
How to allow null, but forbid undefined?
例如对于数据库行,我们可能需要不能未定义的可空属性:
class DbRow {
@IsNumber()
id!: number;
@IsNumber()
numNullable!: number | null;
}
所以 numNullable
可以是数字或 null
- 但绝不能是 undefined
.
我们如何在 class-validator 中表达这个?
- 添加
@Optional()
不起作用,因为那样也会允许 undefined
- 我也没有使用自定义验证器
这是库的限制,它不允许条件分支。
最好的方法是编写自己的只允许空值的验证器。
事实证明,这可以通过使用 conditional validation ValidateIf
:
class DbRow {
@IsNumber()
id!: number;
@IsNumber()
@ValidateIf((object, value) => value !== null)
numNullable!: number | null;
}
这是我的解决方案:
import { ValidationOptions, ValidateIf } from 'class-validator';
export function IsNullable(validationOptions?: ValidationOptions) {
return ValidateIf((_object, value) => value !== null, validationOptions);
}
用法
import { plainToClass } from 'class-transformer';
import { IsNumber, validateSync } from 'class-validator';
import { IsNullable } from 'src/common/utils/is-nullable.decorator';
class SampleDto {
@IsNullable()
@IsNumber()
foo: number | null;
}
describe('IsNullable', () => {
it('should disable other validators when given property is null', () => {
expect(validateSync(plainToClass(SampleDto, { foo: null }))).toEqual([]);
});
it('should allow other validators to work when given property is not null', () => {
expect(validateSync(plainToClass(SampleDto, { foo: 1 }))).toEqual([]);
expect(validateSync(plainToClass(SampleDto, { foo: '1' }))[0].constraints.isNumber).toMatch('foo must be a number');
});
it('should not allow undefined', () => {
expect(validateSync(plainToClass(SampleDto, { foo: undefined })).length).toBeGreaterThan(0);
});
});
这是从 class-validator
导出的 IsOptional
的扩展版本。
import {
ValidationOptions,
ValidateIf,
IsOptional as IsOptionalValidator,
} from 'class-validator';
/**
* Checks if value is missing and if so, ignores all validators.
*
* @param nullable If `true`, all other validators will be skipped even when the value is `null`. `false` by default.
* @param validationOptions {@link ValidationOptions}
*
* @see IsOptional exported from `class-validator.
*/
export function IsOptional(
nullable = false,
validationOptions?: ValidationOptions,
) {
if (nullable) {
return IsOptionalValidator(validationOptions);
}
return ValidateIf((ob: any, v: any) => {
return v !== undefined;
}, validationOptions);
}
例如对于数据库行,我们可能需要不能未定义的可空属性:
class DbRow {
@IsNumber()
id!: number;
@IsNumber()
numNullable!: number | null;
}
所以 numNullable
可以是数字或 null
- 但绝不能是 undefined
.
我们如何在 class-validator 中表达这个?
- 添加
@Optional()
不起作用,因为那样也会允许undefined
- 我也没有使用自定义验证器
这是库的限制,它不允许条件分支。 最好的方法是编写自己的只允许空值的验证器。
事实证明,这可以通过使用 conditional validation ValidateIf
:
class DbRow {
@IsNumber()
id!: number;
@IsNumber()
@ValidateIf((object, value) => value !== null)
numNullable!: number | null;
}
这是我的解决方案:
import { ValidationOptions, ValidateIf } from 'class-validator';
export function IsNullable(validationOptions?: ValidationOptions) {
return ValidateIf((_object, value) => value !== null, validationOptions);
}
用法
import { plainToClass } from 'class-transformer';
import { IsNumber, validateSync } from 'class-validator';
import { IsNullable } from 'src/common/utils/is-nullable.decorator';
class SampleDto {
@IsNullable()
@IsNumber()
foo: number | null;
}
describe('IsNullable', () => {
it('should disable other validators when given property is null', () => {
expect(validateSync(plainToClass(SampleDto, { foo: null }))).toEqual([]);
});
it('should allow other validators to work when given property is not null', () => {
expect(validateSync(plainToClass(SampleDto, { foo: 1 }))).toEqual([]);
expect(validateSync(plainToClass(SampleDto, { foo: '1' }))[0].constraints.isNumber).toMatch('foo must be a number');
});
it('should not allow undefined', () => {
expect(validateSync(plainToClass(SampleDto, { foo: undefined })).length).toBeGreaterThan(0);
});
});
这是从 class-validator
导出的 IsOptional
的扩展版本。
import {
ValidationOptions,
ValidateIf,
IsOptional as IsOptionalValidator,
} from 'class-validator';
/**
* Checks if value is missing and if so, ignores all validators.
*
* @param nullable If `true`, all other validators will be skipped even when the value is `null`. `false` by default.
* @param validationOptions {@link ValidationOptions}
*
* @see IsOptional exported from `class-validator.
*/
export function IsOptional(
nullable = false,
validationOptions?: ValidationOptions,
) {
if (nullable) {
return IsOptionalValidator(validationOptions);
}
return ValidateIf((ob: any, v: any) => {
return v !== undefined;
}, validationOptions);
}