Skip to content

Commit

Permalink
Merge pull request #2981 from felixmosh/improve-alternative-types
Browse files Browse the repository at this point in the history
feat: Improve alternatives type
  • Loading branch information
Marsup authored Oct 4, 2023
2 parents 2f8281f + 0fb1126 commit 6dbe77d
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 26 deletions.
49 changes: 37 additions & 12 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ declare namespace Joi {
otherwise: SchemaLike;
}

interface WhenOptions {
interface WhenOptions<ThenSchema = any, OtherwiseSchema = any> {
/**
* the required condition joi type.
*/
Expand All @@ -482,12 +482,12 @@ declare namespace Joi {
/**
* the alternative schema type if the condition is true. Required if otherwise or switch are missing.
*/
then?: SchemaLike;
then?: SchemaLike<ThenSchema>;

/**
* the alternative schema type if the condition is false. Required if then or switch are missing.
*/
otherwise?: SchemaLike;
otherwise?: SchemaLike<OtherwiseSchema>;

/**
* the list of cases. Required if then is missing. Required if then or otherwise are missing.
Expand All @@ -500,15 +500,15 @@ declare namespace Joi {
break?: boolean;
}

interface WhenSchemaOptions {
interface WhenSchemaOptions<ThenSchema = any, OtherwiseSchema = any> {
/**
* the alternative schema type if the condition is true. Required if otherwise is missing.
*/
then?: SchemaLike;
then?: SchemaLike<ThenSchema>;
/**
* the alternative schema type if the condition is false. Required if then is missing.
*/
otherwise?: SchemaLike;
otherwise?: SchemaLike<OtherwiseSchema>;
}

interface Cache {
Expand Down Expand Up @@ -768,8 +768,11 @@ declare namespace Joi {

type ExternalValidationFunction<V = any, R = V> = (value: V, helpers: ExternalHelpers<R>) => R | undefined;

type SchemaLikeWithoutArray = string | number | boolean | null | Schema | SchemaMap;
type SchemaLike = SchemaLikeWithoutArray | object;
type Primitives = string | number | boolean | bigint | symbol | null;

type SchemaLikeWithoutArray<TSchema = any> = TSchema extends any[] ? never :
(Primitives | Schema<TSchema> | SchemaMap<TSchema>);
type SchemaLike<TSchema = any> = SchemaLikeWithoutArray<TSchema> | object;

type NullableType<T> = undefined | null | T

Expand Down Expand Up @@ -1918,8 +1921,8 @@ declare namespace Joi {
/**
* Adds a conditional alternative schema type, either based on another key value, or a schema peeking into the current value.
*/
conditional(ref: string | Reference, options: WhenOptions | WhenOptions[]): this;
conditional(ref: Schema, options: WhenSchemaOptions): this;
conditional<ThenSchema, OtherwiseSchema>(ref: string | Reference, options: WhenOptions | WhenOptions[]): AlternativesSchema<ThenSchema | OtherwiseSchema>;
conditional<ThenSchema, OtherwiseSchema>(ref: Schema, options: WhenSchemaOptions<ThenSchema, OtherwiseSchema>): AlternativesSchema<ThenSchema | OtherwiseSchema>;

/**
* Requires the validated value to match a specific set of the provided alternative.try() schemas.
Expand All @@ -1930,6 +1933,12 @@ declare namespace Joi {
/**
* Adds an alternative schema type for attempting to match against the validated value.
*/
try<A>(a: SchemaLikeWithoutArray<A>): AlternativesSchema<A>;
try<A, B>(a: SchemaLikeWithoutArray<A>, b: SchemaLikeWithoutArray<B>): AlternativesSchema<A | B>;
try<A, B, C>(a: SchemaLikeWithoutArray<A>, b: SchemaLikeWithoutArray<B>, c: SchemaLikeWithoutArray<C>): AlternativesSchema<A | B | C>;
try<A, B, C, D>(a: SchemaLikeWithoutArray<A>, b: SchemaLikeWithoutArray<B>, c: SchemaLikeWithoutArray<C>, d: SchemaLikeWithoutArray<D>): AlternativesSchema<A | B | C| D>;
try<A, B, C, D, E>(a: SchemaLikeWithoutArray<A>, b: SchemaLikeWithoutArray<B>, c: SchemaLikeWithoutArray<C>, d: SchemaLikeWithoutArray<D>, e: SchemaLikeWithoutArray<E>): AlternativesSchema<A | B | C| D | E>;
try<A, B, C, D, E , F>(a: SchemaLikeWithoutArray<A>, b: SchemaLikeWithoutArray<B>, c: SchemaLikeWithoutArray<C>, d: SchemaLikeWithoutArray<D>, e: SchemaLikeWithoutArray<E>, f: SchemaLikeWithoutArray<F>): AlternativesSchema<A | B | C| D | E |F>;
try(...types: SchemaLikeWithoutArray[]): this;
}

Expand Down Expand Up @@ -2141,12 +2150,28 @@ declare namespace Joi {
/**
* Generates a type that will match one of the provided alternative schemas
*/
alternatives<TSchema = any>(types: SchemaLike[]): AlternativesSchema<TSchema>;
alternatives<TSchema = any>(...types: SchemaLike[]): AlternativesSchema<TSchema>;
alternatives<A,B>(params: [SchemaLike<A>,SchemaLike<B>]): AlternativesSchema<A | B>;
alternatives<A,B,C>(params: [SchemaLike<A>, SchemaLike<B>, SchemaLike<C>]): AlternativesSchema<A | B | C>;
alternatives<A,B,C,D>(params: [SchemaLike<A>, SchemaLike<B>, SchemaLike<C>, SchemaLike<D>]): AlternativesSchema<A | B | C | D>;
alternatives<A,B,C,D, E>(params: [SchemaLike<A>,SchemaLike<B>, SchemaLike<C>, SchemaLike<D>, SchemaLike<E>]): AlternativesSchema<A | B | C | D|E>;
alternatives<A,B>(a: SchemaLike<A>,b: SchemaLike<B>): AlternativesSchema<A | B>;
alternatives<A,B,C>(a: SchemaLike<A>,b: SchemaLike<B>, c:SchemaLike<C>): AlternativesSchema<A | B | C>;
alternatives<A,B,C,D>(a: SchemaLike<A>,b: SchemaLike<B>, c:SchemaLike<C>, d: SchemaLike<D>): AlternativesSchema<A | B | C | D>;
alternatives<A,B,C,D, E>(a: SchemaLike<A>,b: SchemaLike<B>, c:SchemaLike<C>, d: SchemaLike<D>, e: SchemaLike<E>): AlternativesSchema<A | B | C | D|E>;
alternatives<TSchema = any>(types: SchemaLike<TSchema>[]): AlternativesSchema<TSchema>;
alternatives<TSchema = any>(...types: SchemaLike<TSchema>[]): AlternativesSchema<TSchema>;

/**
* Alias for `alternatives`
*/
alt<A,B>(params: [SchemaLike<A>,SchemaLike<B>]): AlternativesSchema<A | B>;
alt<A,B,C>(params: [SchemaLike<A>, SchemaLike<B>, SchemaLike<C>]): AlternativesSchema<A | B | C>;
alt<A,B,C,D>(params: [SchemaLike<A>, SchemaLike<B>, SchemaLike<C>, SchemaLike<D>]): AlternativesSchema<A | B | C | D>;
alt<A,B,C,D, E>(params: [SchemaLike<A>,SchemaLike<B>, SchemaLike<C>, SchemaLike<D>, SchemaLike<E>]): AlternativesSchema<A | B | C | D|E>;
alt<A,B>(a: SchemaLike<A>,b: SchemaLike<B>): AlternativesSchema<A | B>;
alt<A,B,C>(a: SchemaLike<A>,b: SchemaLike<B>, c:SchemaLike<C>): AlternativesSchema<A | B | C>;
alt<A,B,C,D>(a: SchemaLike<A>,b: SchemaLike<B>, c:SchemaLike<C>, d: SchemaLike<D>): AlternativesSchema<A | B | C | D>;
alt<A,B,C,D, E>(a: SchemaLike<A>,b: SchemaLike<B>, c:SchemaLike<C>, d: SchemaLike<D>, e: SchemaLike<E>): AlternativesSchema<A | B | C | D|E>;
alt<TSchema = any>(types: SchemaLike[]): AlternativesSchema<TSchema>;
alt<TSchema = any>(...types: SchemaLike[]): AlternativesSchema<TSchema>;

Expand Down
59 changes: 45 additions & 14 deletions test/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as Joi from '..';
import * as Lab from '@hapi/lab';
import * as Joi from '..';


const { expect } = Lab.types;
Expand All @@ -19,7 +19,8 @@ const exp: RegExp = /./;
const obj: object = {};
const date: Date = new Date();
const err: Error = new Error('test');
const func: () => void = () => {};
const func: () => void = () => {
};
const symbol = Symbol('test');

const numArr: number[] = [1, 2, 3];
Expand Down Expand Up @@ -1011,16 +1012,23 @@ schema = Joi.alternatives();
expect.error(Joi.alternatives().try(schemaArr));
schema = Joi.alternatives().try(schema, schema);

expect.type<Joi.AlternativesSchema<string | number | boolean>>(Joi.alternatives([Joi.string(), Joi.number(), Joi.boolean()]));

schema = Joi.alternatives(schemaArr);
schema = Joi.alternatives(schema, anySchema, boolSchema);

schema = Joi.alt();
expect.error(Joi.alt().try(schemaArr));
schema = Joi.alt().try(schema, schema);

expect.type<Joi.AlternativesSchema<string | number | boolean>>(Joi.alt([Joi.string(), Joi.number(), Joi.boolean()]));

schema = Joi.alt(schemaArr);
schema = Joi.alt(schema, anySchema, boolSchema);

expect.type<Joi.AlternativesSchema<string | number | boolean>>(Joi.alternatives().try(Joi.string(), Joi.number(), Joi.boolean()));


// --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---

schema = Joi.link(str);
Expand Down Expand Up @@ -1056,18 +1064,41 @@ schema = Joi.link(str);
.then((val) => {
throw new Error('one error');
})
.catch((e) => {});

expect.type<Promise<TResult>>(schema.validateAsync(value));
expect.type<Promise<{ value: TResult, artifacts: Map<any, string[][]> }>>(schema.validateAsync(value, { artifacts: true }));
expect.type<Promise<{ value: TResult, warning: Joi.ValidationWarning }>>(schema.validateAsync(value, { warnings: true }));
expect.type<Promise<{ value: TResult, artifacts: Map<any, string[][]>; warning: Joi.ValidationWarning }>>(schema.validateAsync(value, { artifacts: true, warnings: true }));
expect.error<Promise<{ value: TResult, warning: Joi.ValidationWarning }>>(schema.validateAsync(value, { artifacts: true }));
expect.error<Promise<{ value: TResult, artifacts: Map<any, string[][]> }>>(schema.validateAsync(value, { warnings: true }));
expect.error<Promise<TResult>>(schema.validateAsync(value, { artifacts: true, warnings: true }));
expect.type<Promise<TResult>>(schema.validateAsync(value, { artifacts: false }));
expect.type<Promise<TResult>>(schema.validateAsync(value, { warnings: false }));
expect.type<Promise<TResult>>(schema.validateAsync(value, { artifacts: false, warnings: false }));
.catch((e) => {
});

expect.type<Promise<TResult>>(schema.validateAsync(value));
expect.type<Promise<{
value: TResult,
artifacts: Map<any, string[][]>
}>>(schema.validateAsync(value, { artifacts: true }));
expect.type<Promise<{
value: TResult,
warning: Joi.ValidationWarning
}>>(schema.validateAsync(value, { warnings: true }));
expect.type<Promise<{
value: TResult,
artifacts: Map<any, string[][]>;
warning: Joi.ValidationWarning
}>>(schema.validateAsync(value, { artifacts: true, warnings: true }));
expect.error<Promise<{
value: TResult,
warning: Joi.ValidationWarning
}>>(schema.validateAsync(value, { artifacts: true }));
expect.error<Promise<{
value: TResult,
artifacts: Map<any, string[][]>
}>>(schema.validateAsync(value, { warnings: true }));
expect.error<Promise<TResult>>(schema.validateAsync(value, {
artifacts: true,
warnings: true
}));
expect.type<Promise<TResult>>(schema.validateAsync(value, { artifacts: false }));
expect.type<Promise<TResult>>(schema.validateAsync(value, { warnings: false }));
expect.type<Promise<TResult>>(schema.validateAsync(value, {
artifacts: false,
warnings: false
}));

const falsyValue = { username: 'example' };
result = schema.validate(falsyValue);
Expand Down

0 comments on commit 6dbe77d

Please sign in to comment.