fluentvalidation-ts
Version:
A TypeScript-first library for building strongly-typed validation rules
158 lines (137 loc) • 12.9 kB
text/typescript
type ArrayType<TEachValue> = Array<TEachValue> | ReadonlyArray<TEachValue> | Readonly<Array<TEachValue>>;
type ValueValidationResult<TValue> = (TValue extends ArrayType<infer TEachValue> ? Array<ValueValidationResult<TEachValue>> | string | null : TValue extends object ? {
[propertyName in keyof TValue]?: ValueValidationResult<TValue[propertyName]>;
} | string | null : string | null) | string | null;
type ValidationErrors<TModel> = {
[propertyName in keyof TModel]?: ValueValidationResult<TModel[propertyName]>;
};
type AppliesTo = 'AppliesToAllValidators' | 'AppliesToCurrentValidator';
type MessageGenerator<TModel, TTransformedValue> = (value: TTransformedValue, model: TModel) => string;
type Message<TModel, TTransformedValue> = string | MessageGenerator<TModel, TTransformedValue>;
type SimplePredicate<TModel, TTransformedValue> = (value: TTransformedValue, model: TModel) => boolean;
type SimpleAsyncPredicate<TModel, TTransformedValue> = (value: TTransformedValue, model: TModel) => Promise<boolean>;
type SimplePredicateWithMessage<TModel, TTransformedValue> = {
predicate: SimplePredicate<TModel, TTransformedValue>;
message: Message<TModel, TTransformedValue>;
};
type SimpleAsyncPredicateWithMessage<TModel, TTransformedValue> = {
predicate: SimpleAsyncPredicate<TModel, TTransformedValue>;
message: Message<TModel, TTransformedValue>;
};
type Predicate<TModel, TTransformedValue> = SimplePredicate<TModel, TTransformedValue> | SimplePredicateWithMessage<TModel, TTransformedValue> | Array<SimplePredicate<TModel, TTransformedValue> | SimplePredicateWithMessage<TModel, TTransformedValue>>;
type AsyncPredicate<TModel, TTransformedValue> = SimpleAsyncPredicate<TModel, TTransformedValue> | SimpleAsyncPredicateWithMessage<TModel, TTransformedValue> | Array<SimpleAsyncPredicate<TModel, TTransformedValue> | SimpleAsyncPredicateWithMessage<TModel, TTransformedValue>>;
interface IValidator<TModel> {
validate: (model: TModel) => ValidationErrors<TModel>;
}
type IfString<TValue, TOut> = TValue extends string ? TOut : never;
type IfNumber<TValue, TOut> = TValue extends number ? TOut : never;
type IfObject<TValue, TOut> = TValue extends object ? TOut : never;
type NotNullRuleOptions = {
includeUndefined?: boolean;
};
type NullRuleOptions = {
includeUndefined: boolean;
};
type RuleValidators<TModel, TValue> = {
notEqual: (forbiddenValue: TValue) => RuleValidatorsAndExtensions<TModel, TValue>;
equal: (requiredValue: TValue) => RuleValidatorsAndExtensions<TModel, TValue>;
must: (predicate: Predicate<TModel, TValue>) => RuleValidatorsAndExtensions<TModel, TValue>;
notNull: (options?: NotNullRuleOptions) => RuleValidatorsAndExtensions<TModel, TValue>;
notUndefined: () => RuleValidatorsAndExtensions<TModel, TValue>;
null: (ruleOptions?: NullRuleOptions) => RuleValidatorsAndExtensions<TModel, TValue>;
undefined: () => RuleValidatorsAndExtensions<TModel, TValue>;
notEmpty: IfString<TValue, () => RuleValidatorsAndExtensions<TModel, TValue>>;
length: IfString<TValue, (minLength: number, maxLength: number) => RuleValidatorsAndExtensions<TModel, TValue>>;
maxLength: IfString<TValue, (maxLength: number) => RuleValidatorsAndExtensions<TModel, TValue>>;
minLength: IfString<TValue, (minLength: number) => RuleValidatorsAndExtensions<TModel, TValue>>;
matches: IfString<TValue, (pattern: RegExp) => RuleValidatorsAndExtensions<TModel, TValue>>;
emailAddress: IfString<TValue, () => RuleValidatorsAndExtensions<TModel, TValue>>;
lessThan: IfNumber<TValue, (threshold: number) => RuleValidatorsAndExtensions<TModel, TValue>>;
lessThanOrEqualTo: IfNumber<TValue, (threshold: number) => RuleValidatorsAndExtensions<TModel, TValue>>;
greaterThan: IfNumber<TValue, (threshold: number) => RuleValidatorsAndExtensions<TModel, TValue>>;
greaterThanOrEqualTo: IfNumber<TValue, (threshold: number) => RuleValidatorsAndExtensions<TModel, TValue>>;
exclusiveBetween: IfNumber<TValue, (lowerBound: number, upperBound: number) => RuleValidatorsAndExtensions<TModel, TValue>>;
inclusiveBetween: IfNumber<TValue, (lowerBound: number, upperBound: number) => RuleValidatorsAndExtensions<TModel, TValue>>;
precisionScale: IfNumber<TValue, (precision: number, scale: number) => RuleValidatorsAndExtensions<TModel, TValue>>;
setValidator: IfObject<TValue, (validatorProducer: (model: TModel) => IValidator<NonNullable<TValue>>) => RuleValidatorsAndExtensions<TModel, TValue>>;
};
type WhenCondition<TModel, TValue> = (condition: (model: TModel) => boolean, appliesTo?: AppliesTo) => RuleValidators<TModel, TValue>;
type UnlessCondition<TModel, TValue> = (condition: (model: TModel) => boolean, appliesTo?: AppliesTo) => RuleValidators<TModel, TValue>;
type RuleValidatorsAndConditionExtensions<TModel, TValue> = RuleValidators<TModel, TValue> & {
when: WhenCondition<TModel, TValue>;
unless: UnlessCondition<TModel, TValue>;
};
type WithMessage<TModel, TValue> = (message: string) => RuleValidatorsAndConditionExtensions<TModel, TValue>;
type RuleValidatorsAndExtensions<TModel, TValue> = RuleValidatorsAndConditionExtensions<TModel, TValue> & {
withMessage: WithMessage<TModel, TValue>;
};
/**
* Constrain
* @desc Constrains type `T` to only those properties that exist in type `U`
*/
type Constrain<T, U> = {
[key in keyof T]: key extends keyof U ? T[key] : never;
};
type FlatType = string | number | boolean | symbol;
type TransformedValue<TValue> = TValue | FlatType | null | undefined;
type Optional<T> = T | null | undefined;
type IfNotNeverThen<TValue, TOut> = TValue extends never ? never : TOut;
declare class SyncValidator<TModel> {
private valueValidatorBuildersByPropertyName;
protected _validate: (value: TModel) => ValidationErrors<TModel>;
validate: (value: TModel) => ValidationErrors<TModel>;
private rebuildValidate;
protected ruleFor: <TPropertyName extends keyof TModel, TValue extends TModel[TPropertyName]>(propertyName: TPropertyName) => RuleValidators<TModel, TValue>;
protected ruleForTransformed: <TPropertyName extends keyof TModel, TValue extends TModel[TPropertyName], TTransformedValue extends TransformedValue<TValue>>(propertyName: TPropertyName, transformValue: (value: TValue) => TTransformedValue extends object ? Constrain<TTransformedValue, TValue> : TTransformedValue) => RuleValidators<TModel, TTransformedValue>;
protected ruleForEach: <TPropertyName extends keyof TModel, TEachValue extends TModel[TPropertyName] extends Optional<ArrayType<infer TEachValueInferred>> ? TEachValueInferred : never, TValue extends TModel[TPropertyName] & ArrayType<TEachValue>>(propertyName: IfNotNeverThen<TEachValue, TPropertyName>) => IfNotNeverThen<TEachValue, RuleValidators<TModel, TEachValue>>;
protected ruleForEachTransformed: <TPropertyName extends keyof TModel, TEachValue extends TModel[TPropertyName] extends Optional<ArrayType<infer TEachValueInferred>> ? TEachValueInferred : never, TValue extends TModel[TPropertyName] & ArrayType<TEachValue>, TEachTransformedValue extends TransformedValue<TEachValue>>(propertyName: IfNotNeverThen<TEachValue, TPropertyName>, transformValue: (value: TEachValue) => TEachTransformedValue extends object ? Constrain<TEachTransformedValue, TEachValue> : TEachTransformedValue) => IfNotNeverThen<TEachValue, RuleValidators<TModel, TEachTransformedValue>>;
}
interface IAsyncValidator<TModel> {
validateAsync: (model: TModel) => Promise<ValidationErrors<TModel>>;
}
type AsyncRuleValidators<TModel, TValue> = {
notEqual: (forbiddenValue: TValue) => AsyncRuleValidatorsAndExtensions<TModel, TValue>;
equal: (requiredValue: TValue) => AsyncRuleValidatorsAndExtensions<TModel, TValue>;
must: (predicate: Predicate<TModel, TValue>) => AsyncRuleValidatorsAndExtensions<TModel, TValue>;
mustAsync: (predicate: AsyncPredicate<TModel, TValue>) => AsyncRuleValidatorsAndExtensions<TModel, TValue>;
notNull: (ruleOptions?: NotNullRuleOptions) => AsyncRuleValidatorsAndExtensions<TModel, TValue>;
notUndefined: () => AsyncRuleValidatorsAndExtensions<TModel, TValue>;
null: (ruleOptions?: NullRuleOptions) => AsyncRuleValidatorsAndExtensions<TModel, TValue>;
undefined: () => AsyncRuleValidatorsAndExtensions<TModel, TValue>;
notEmpty: IfString<TValue, () => AsyncRuleValidatorsAndExtensions<TModel, TValue>>;
length: IfString<TValue, (minLength: number, maxLength: number) => AsyncRuleValidatorsAndExtensions<TModel, TValue>>;
maxLength: IfString<TValue, (maxLength: number) => AsyncRuleValidatorsAndExtensions<TModel, TValue>>;
minLength: IfString<TValue, (minLength: number) => AsyncRuleValidatorsAndExtensions<TModel, TValue>>;
matches: IfString<TValue, (pattern: RegExp) => AsyncRuleValidatorsAndExtensions<TModel, TValue>>;
emailAddress: IfString<TValue, () => AsyncRuleValidatorsAndExtensions<TModel, TValue>>;
lessThan: IfNumber<TValue, (threshold: number) => AsyncRuleValidatorsAndExtensions<TModel, TValue>>;
lessThanOrEqualTo: IfNumber<TValue, (threshold: number) => AsyncRuleValidatorsAndExtensions<TModel, TValue>>;
greaterThan: IfNumber<TValue, (threshold: number) => AsyncRuleValidatorsAndExtensions<TModel, TValue>>;
greaterThanOrEqualTo: IfNumber<TValue, (threshold: number) => AsyncRuleValidatorsAndExtensions<TModel, TValue>>;
exclusiveBetween: IfNumber<TValue, (lowerBound: number, upperBound: number) => AsyncRuleValidatorsAndExtensions<TModel, TValue>>;
inclusiveBetween: IfNumber<TValue, (lowerBound: number, upperBound: number) => AsyncRuleValidatorsAndExtensions<TModel, TValue>>;
precisionScale: IfNumber<TValue, (precision: number, scale: number) => AsyncRuleValidatorsAndExtensions<TModel, TValue>>;
setValidator: IfObject<TValue, (validatorProducer: (model: TModel) => IValidator<NonNullable<TValue>>) => AsyncRuleValidatorsAndExtensions<TModel, TValue>>;
setAsyncValidator: IfObject<TValue, (validatorProducer: (model: TModel) => IAsyncValidator<NonNullable<TValue>>) => AsyncRuleValidatorsAndExtensions<TModel, TValue>>;
};
type AsyncWhenCondition<TModel, TValue> = (condition: (model: TModel) => boolean, appliesTo?: AppliesTo) => AsyncRuleValidators<TModel, TValue>;
type AsyncUnlessCondition<TModel, TValue> = (condition: (model: TModel) => boolean, appliesTo?: AppliesTo) => AsyncRuleValidators<TModel, TValue>;
type AsyncRuleValidatorsAndConditionExtensions<TModel, TValue> = AsyncRuleValidators<TModel, TValue> & {
when: AsyncWhenCondition<TModel, TValue>;
unless: AsyncUnlessCondition<TModel, TValue>;
};
type AsyncWithMessage<TModel, TValue> = (message: string) => AsyncRuleValidatorsAndConditionExtensions<TModel, TValue>;
type AsyncRuleValidatorsAndExtensions<TModel, TValue> = AsyncRuleValidatorsAndConditionExtensions<TModel, TValue> & {
withMessage: AsyncWithMessage<TModel, TValue>;
};
declare class AsyncValidator<TModel> {
private asyncValueValidatorBuildersByPropertyName;
protected _validateAsync: (value: TModel) => Promise<ValidationErrors<TModel>>;
validateAsync: (value: TModel) => Promise<ValidationErrors<TModel>>;
private rebuildValidateAsync;
protected ruleFor: <TPropertyName extends keyof TModel, TValue extends TModel[TPropertyName]>(propertyName: TPropertyName) => AsyncRuleValidators<TModel, TValue>;
protected ruleForTransformed: <TPropertyName extends keyof TModel, TValue extends TModel[TPropertyName], TTransformedValue extends TransformedValue<TValue>>(propertyName: TPropertyName, transformValue: (value: TValue) => TTransformedValue extends object ? Constrain<TTransformedValue, TValue> : TTransformedValue) => AsyncRuleValidators<TModel, TTransformedValue>;
protected ruleForEach: <TPropertyName extends keyof TModel, TEachValue extends TModel[TPropertyName] extends Optional<ArrayType<infer TEachValueInferred>> ? TEachValueInferred : never, TValue extends TModel[TPropertyName] & ArrayType<TEachValue>>(propertyName: IfNotNeverThen<TEachValue, TPropertyName>) => IfNotNeverThen<TEachValue, AsyncRuleValidators<TModel, TEachValue>>;
protected ruleForEachTransformed: <TPropertyName extends keyof TModel, TEachValue extends TModel[TPropertyName] extends Optional<ArrayType<infer TEachValueInferred>> ? TEachValueInferred : never, TValue extends TModel[TPropertyName] & ArrayType<TEachValue>, TEachTransformedValue extends TransformedValue<TEachValue>>(propertyName: IfNotNeverThen<TEachValue, TPropertyName>, transformValue: (value: TEachValue) => TEachTransformedValue extends object ? Constrain<TEachTransformedValue, TEachValue> : TEachTransformedValue) => IfNotNeverThen<TEachValue, AsyncRuleValidators<TModel, TEachTransformedValue>>;
}
export { type AsyncRuleValidators, AsyncValidator, type RuleValidators, type ValidationErrors, SyncValidator as Validator, type ValueValidationResult };