Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Improve alternatives type #2981

Merged
merged 5 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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