-
-
Save panzelva/b0e565870c950c4cfd77188d69a3064a to your computer and use it in GitHub Desktop.
/* eslint-disable @typescript-eslint/no-explicit-any */ | |
// eslint-disable-next-line @typescript-eslint/no-unused-vars | |
import * as joi from 'joi' | |
type ArrayType<T> = T extends (infer U)[] ? U : never | |
declare module 'joi' { | |
interface Root { | |
extend(...extensions: Array<joi.Extension | joi.ExtensionFactory>): this | |
any<T extends any>(): BoxAnySchema<Box<T, false>> | |
string<T extends string>(): BoxStringSchema<Box<T, false>> | |
number<T extends number>(): BoxNumberSchema<Box<T, false>> | |
boolean<T extends boolean>(): BoxBooleanSchema<Box<T, false>> | |
date<T extends Date>(): BoxDateSchema<Box<T, false>> | |
// eslint-disable-next-line @typescript-eslint/ban-types | |
func<T extends Function>(): BoxFunctionSchema<Box<T, false>> | |
array(): BoxArraySchema<Box<never, false>> | |
object<T extends mappedSchemaMap>(schema?: T): BoxObjectSchema<Box<extractMap<T>, false>> | |
alternatives<T extends mappedSchema[]>( | |
...alts: T | |
): BoxAlternativesSchema<Box<extractType<typeof alts[number]>, false>> | |
alternatives<T extends mappedSchema[]>( | |
alts: T | |
): BoxAlternativesSchema<Box<extractType<typeof alts[number]>, false>> | |
alt<T extends mappedSchema[]>( | |
...alts: T | |
): BoxAlternativesSchema<Box<extractType<typeof alts[number]>, false>> | |
alt<T extends mappedSchema[]>( | |
alts: T | |
): BoxAlternativesSchema<Box<extractType<typeof alts[number]>, false>> | |
} | |
/** | |
* Field requirements interface | |
*/ | |
interface Box<T, R extends boolean> { | |
/** Type the schema holds */ | |
T: T | |
/** If this attribute is required when inside an object */ | |
R: R | |
} | |
// Operators | |
type BoxType<B, nT> = B extends Box<infer oT, infer oR> ? Box<nT, oR> : B | |
type BoxUnion<B, nT> = B extends Box<infer oT, infer oR> ? Box<oT | nT, oR> : B | |
type BoxIntersection<B, nT> = B extends Box<infer oT, infer oR> ? Box<oT & nT, oR> : B | |
type BoxReq<B, nR extends boolean> = B extends Box<infer oT, infer oR> ? Box<oT, nR> : B | |
type BoxSchema = Box<any, boolean> | |
// eslint-disable-next-line @typescript-eslint/ban-types | |
type primitiveType = string | number | boolean | Function | Date | undefined | null | void | |
type mappedSchema = joi.SchemaLike | BoxedPrimitive | |
type mappedSchemaMap = { [K: string]: mappedSchema } | |
type extendsGuard<T, S> = S extends T ? S : T | |
/** | |
* Every Schema that implements the Box to allow the extraction | |
*/ | |
type BoxedPrimitive<T extends BoxSchema = any> = | |
| BoxAnySchema<T> | |
| BoxStringSchema<T> | |
| BoxNumberSchema<T> | |
| BoxBooleanSchema<T> | |
| BoxDateSchema<T> | |
| BoxFunctionSchema<T> | |
| BoxArraySchema<T> | |
| BoxObjectSchema<T> | |
| BoxAlternativesSchema<T> | |
interface BoxAnySchema<N extends Box<any, boolean>> extends joi.AnySchema { | |
__schemaTypeLiteral: 'BoxAnySchema' | |
default<T>( | |
value: T, | |
description?: string | |
): this extends BoxAnySchema<infer B> ? BoxAnySchema<BoxUnion<B, T>> : never | |
default(value: any, description?: string): this | |
default(): this | |
allow<T>( | |
...values: T[] | |
): this extends BoxAnySchema<infer B> ? BoxAnySchema<BoxUnion<B, T>> : never | |
allow<T>(values: T[]): this extends BoxAnySchema<infer B> ? BoxAnySchema<BoxUnion<B, T>> : never | |
allow(...values: any[]): this | |
allow(values: any[]): this | |
valid<T>( | |
...values: T[] | |
): this extends BoxAnySchema<infer B> ? BoxAnySchema<BoxType<B, T>> : never | |
valid<T>(values: T[]): this extends BoxAnySchema<infer B> ? BoxAnySchema<BoxType<B, T>> : never | |
valid(...values: any[]): this | |
valid(values: any[]): this | |
required(): this extends BoxAnySchema<infer B> ? BoxAnySchema<BoxReq<B, true>> : never | |
required(): this | |
exist(): this extends BoxAnySchema<infer B> ? BoxAnySchema<BoxReq<B, true>> : never | |
exist(): this | |
optional(): this extends BoxAnySchema<infer B> ? BoxAnySchema<BoxReq<B, false>> : never | |
optional(): this | |
} | |
interface BoxStringSchema<N extends BoxSchema> extends joi.StringSchema { | |
__schemaTypeLiteral: 'BoxStringSchema' | |
default<T extends string>( | |
value: T, | |
description?: string | |
): this extends BoxStringSchema<infer B> ? BoxStringSchema<BoxUnion<B, T>> : never | |
default(value: any, description?: string): this | |
default(): this | |
allow<T>( | |
...values: T[] | |
): this extends BoxStringSchema<infer B> ? BoxStringSchema<BoxUnion<B, T>> : never | |
allow<T>( | |
values: T[] | |
): this extends BoxStringSchema<infer B> ? BoxStringSchema<BoxUnion<B, T>> : never | |
allow(...values: any[]): this | |
allow(values: any[]): this | |
valid<T extends string>( | |
...values: T[] | |
): this extends BoxStringSchema<infer B> ? BoxStringSchema<BoxType<B, T>> : never | |
valid<T extends string>( | |
values: T[] | |
): this extends BoxStringSchema<infer B> ? BoxStringSchema<BoxType<B, T>> : never | |
valid(...values: any[]): this | |
valid(values: any[]): this | |
required(): this extends BoxStringSchema<infer B> ? BoxStringSchema<BoxReq<B, true>> : never | |
required(): this | |
exist(): this extends BoxStringSchema<infer B> ? BoxStringSchema<BoxReq<B, true>> : never | |
exist(): this | |
optional(): this extends BoxStringSchema<infer B> ? BoxStringSchema<BoxReq<B, false>> : never | |
optional(): this | |
} | |
interface BoxNumberSchema<N extends BoxSchema> extends joi.NumberSchema { | |
__schemaTypeLiteral: 'BoxNumberSchema' | |
default<T extends number>( | |
value: T, | |
description?: string | |
): this extends BoxNumberSchema<infer B> ? BoxNumberSchema<BoxUnion<B, T>> : never | |
default(value: any, description?: string): this | |
default(): this | |
allow<T>( | |
...values: T[] | |
): this extends BoxNumberSchema<infer B> ? BoxNumberSchema<BoxUnion<B, T>> : never | |
allow<T>( | |
values: T[] | |
): this extends BoxNumberSchema<infer B> ? BoxNumberSchema<BoxUnion<B, T>> : never | |
allow(...values: any[]): this | |
allow(values: any[]): this | |
valid<T extends string>( | |
...values: T[] | |
): this extends BoxNumberSchema<infer B> ? BoxNumberSchema<BoxType<B, T>> : never | |
valid<T extends string>( | |
values: T[] | |
): this extends BoxNumberSchema<infer B> ? BoxNumberSchema<BoxType<B, T>> : never | |
valid(...values: any[]): this | |
valid(values: any[]): this | |
required(): this extends BoxNumberSchema<infer B> ? BoxNumberSchema<BoxReq<B, true>> : never | |
required(): this | |
exist(): this extends BoxNumberSchema<infer B> ? BoxNumberSchema<BoxReq<B, true>> : never | |
exist(): this | |
optional(): this extends BoxNumberSchema<infer B> ? BoxNumberSchema<BoxReq<B, false>> : never | |
optional(): this | |
} | |
interface BoxBooleanSchema<N extends BoxSchema> extends joi.BooleanSchema { | |
__schemaTypeLiteral: 'BoxBooleanSchema' | |
default<T extends boolean>( | |
value: T, | |
description?: string | |
): this extends BoxBooleanSchema<infer B> ? BoxBooleanSchema<BoxUnion<B, T>> : never | |
default(value: any, description?: string): this | |
default(): this | |
allow<T>( | |
...values: T[] | |
): this extends BoxBooleanSchema<infer B> ? BoxBooleanSchema<BoxUnion<B, T>> : never | |
allow<T>( | |
values: T[] | |
): this extends BoxBooleanSchema<infer B> ? BoxBooleanSchema<BoxUnion<B, T>> : never | |
allow(...values: any[]): this | |
allow(values: any[]): this | |
valid<T extends string>( | |
...values: T[] | |
): this extends BoxBooleanSchema<infer B> ? BoxBooleanSchema<BoxType<B, T>> : never | |
valid<T extends string>( | |
values: T[] | |
): this extends BoxBooleanSchema<infer B> ? BoxBooleanSchema<BoxType<B, T>> : never | |
valid(...values: any[]): this | |
valid(values: any[]): this | |
required(): this extends BoxBooleanSchema<infer B> ? BoxBooleanSchema<BoxReq<B, true>> : never | |
required(): this | |
exist(): this extends BoxBooleanSchema<infer B> ? BoxBooleanSchema<BoxReq<B, true>> : never | |
exist(): this | |
optional(): this extends BoxBooleanSchema<infer B> ? BoxBooleanSchema<BoxReq<B, false>> : never | |
optional(): this | |
} | |
interface BoxDateSchema<N extends BoxSchema> extends joi.DateSchema { | |
__schemaTypeLiteral: 'BoxDateSchema' | |
default<T extends Date>( | |
value: T, | |
description?: string | |
): this extends BoxDateSchema<infer B> ? BoxDateSchema<BoxUnion<B, T>> : never | |
default(value: any, description?: string): this | |
default(): this | |
allow<T>( | |
...values: T[] | |
): this extends BoxDateSchema<infer B> ? BoxDateSchema<BoxUnion<B, T>> : never | |
allow<T>( | |
values: T[] | |
): this extends BoxDateSchema<infer B> ? BoxDateSchema<BoxUnion<B, T>> : never | |
allow(...values: any[]): this | |
allow(values: any[]): this | |
valid<T extends string>( | |
...values: T[] | |
): this extends BoxDateSchema<infer B> ? BoxDateSchema<BoxType<B, T>> : never | |
valid<T extends string>( | |
values: T[] | |
): this extends BoxDateSchema<infer B> ? BoxDateSchema<BoxType<B, T>> : never | |
valid(...values: any[]): this | |
valid(values: any[]): this | |
required(): this extends BoxDateSchema<infer B> ? BoxDateSchema<BoxReq<B, true>> : never | |
required(): this | |
exist(): this extends BoxDateSchema<infer B> ? BoxDateSchema<BoxReq<B, true>> : never | |
exist(): this | |
optional(): this extends BoxDateSchema<infer B> ? BoxDateSchema<BoxReq<B, false>> : never | |
optional(): this | |
} | |
interface BoxFunctionSchema<N extends BoxSchema> extends joi.FunctionSchema { | |
__schemaTypeLiteral: 'BoxFunctionSchema' | |
allow<T>( | |
...values: T[] | |
): this extends BoxFunctionSchema<infer B> ? BoxFunctionSchema<BoxUnion<B, T>> : never | |
allow<T>( | |
values: T[] | |
): this extends BoxFunctionSchema<infer B> ? BoxFunctionSchema<BoxUnion<B, T>> : never | |
allow(...values: any[]): this | |
allow(values: any[]): this | |
required(): this extends BoxFunctionSchema<infer B> ? BoxFunctionSchema<BoxReq<B, true>> : never | |
required(): this | |
exist(): this extends BoxFunctionSchema<infer B> ? BoxFunctionSchema<BoxReq<B, true>> : never | |
exist(): this | |
optional(): this extends BoxFunctionSchema<infer B> | |
? BoxFunctionSchema<BoxReq<B, false>> | |
: never | |
optional(): this | |
} | |
interface BoxArraySchema<N extends BoxSchema> extends joi.ArraySchema { | |
__schemaTypeLiteral: 'BoxArraySchema' | |
default<T extends any[]>( | |
value: T, | |
description?: string | |
): this extends BoxArraySchema<infer B> ? BoxArraySchema<BoxUnion<B, ArrayType<T>>> : never | |
default(value: any, description?: string): this | |
default(): this | |
allow<T>( | |
...values: T[] | |
): this extends BoxArraySchema<infer B> ? BoxArraySchema<BoxUnion<B, T>> : never | |
allow<T>( | |
values: T[] | |
): this extends BoxArraySchema<infer B> ? BoxArraySchema<BoxUnion<B, T>> : never | |
allow(...values: any[]): this | |
allow(values: any[]): this | |
items<T extends mappedSchema>( | |
type: T | |
): this extends BoxArraySchema<infer B> ? BoxArraySchema<BoxUnion<B, extractType<T>>> : never | |
items(...types: joi.SchemaLike[]): this | |
items(types: joi.SchemaLike[]): this | |
required(): this extends BoxArraySchema<infer B> ? BoxArraySchema<BoxReq<B, true>> : never | |
required(): this | |
exist(): this extends BoxArraySchema<infer B> ? BoxArraySchema<BoxReq<B, true>> : never | |
exist(): this | |
optional(): this extends BoxArraySchema<infer B> ? BoxArraySchema<BoxReq<B, false>> : never | |
optional(): this | |
} | |
interface BoxObjectSchema<N extends BoxSchema> extends joi.ObjectSchema { | |
__schemaTypeLiteral: 'BoxObjectSchema' | |
default<T extends mappedSchemaMap>( | |
value: T, | |
description?: string | |
): this extends BoxObjectSchema<infer B> ? BoxObjectSchema<BoxUnion<B, extractType<T>>> : never | |
default(value: any, description?: string): this | |
default(): this | |
allow<T>( | |
...values: T[] | |
): this extends BoxObjectSchema<infer B> ? BoxObjectSchema<BoxUnion<B, T>> : never | |
allow<T>( | |
values: T[] | |
): this extends BoxObjectSchema<infer B> ? BoxObjectSchema<BoxUnion<B, T>> : never | |
allow(...values: any[]): this | |
allow(values: any[]): this | |
keys<T extends mappedSchemaMap>( | |
schema: T | |
): this extends BoxObjectSchema<infer B> | |
? BoxObjectSchema<BoxIntersection<B, extractMap<T>>> | |
: never | |
keys(schema?: joi.SchemaMap): this | |
append<T extends mappedSchemaMap>( | |
schema: T | |
): this extends BoxObjectSchema<infer B> | |
? BoxObjectSchema<BoxIntersection<B, extractMap<T>>> | |
: never | |
append(schema?: joi.SchemaMap): this | |
pattern<S extends BoxStringSchema<any>, T extends mappedSchema>( | |
pattern: S, | |
schema: T | |
): this extends BoxObjectSchema<infer B> | |
? BoxObjectSchema<BoxIntersection<B, extractMap<{ [key in extractType<S>]: T }>>> | |
: never | |
pattern<T extends mappedSchema>( | |
pattern: RegExp, | |
schema: T | |
): this extends BoxObjectSchema<infer B> | |
? BoxObjectSchema<BoxIntersection<B, extractMap<{ [key: string]: T }>>> | |
: never | |
pattern(pattern: RegExp | joi.SchemaLike, schema: joi.SchemaLike): this | |
required(): this extends BoxObjectSchema<infer B> ? BoxObjectSchema<BoxReq<B, true>> : never | |
required(): this | |
exist(): this extends BoxObjectSchema<infer B> ? BoxObjectSchema<BoxReq<B, true>> : never | |
exist(): this | |
optional(): this extends BoxObjectSchema<infer B> ? BoxObjectSchema<BoxReq<B, false>> : never | |
optional(): this | |
} | |
interface BoxAlternativesSchema<N extends BoxSchema> extends joi.AlternativesSchema { | |
__schemaTypeLiteral: 'BoxAlternativesSchema' | |
allow<T>( | |
...values: T[] | |
): this extends BoxAlternativesSchema<infer B> ? BoxAlternativesSchema<BoxUnion<B, T>> : never | |
allow<T>( | |
values: T[] | |
): this extends BoxAlternativesSchema<infer B> ? BoxAlternativesSchema<BoxUnion<B, T>> : never | |
allow(...values: any[]): this | |
allow(values: any[]): this | |
try<T extends mappedSchema[]>( | |
...values: T | |
): this extends BoxAlternativesSchema<infer O> | |
? O extends Box<infer oT, infer oR> | |
? BoxAlternativesSchema<BoxType<O, oT | extractType<T>>> | |
: BoxAlternativesSchema<Box<extractType<T>, false>> | |
: BoxAlternativesSchema<Box<extractType<T>, false>> | |
try<T extends mappedSchema[]>( | |
values: T | |
): this extends BoxAlternativesSchema<infer O> | |
? O extends Box<infer oT, infer oR> | |
? BoxAlternativesSchema<BoxType<O, oT | extractType<T>>> | |
: BoxAlternativesSchema<Box<extractType<T>, false>> | |
: BoxAlternativesSchema<Box<extractType<T>, false>> | |
try(...types: joi.SchemaLike[]): this | |
try(types: joi.SchemaLike[]): this | |
required(): this extends BoxAlternativesSchema<infer B> | |
? BoxAlternativesSchema<BoxReq<B, true>> | |
: never | |
required(): this | |
exist(): this extends BoxAlternativesSchema<infer B> | |
? BoxAlternativesSchema<BoxReq<B, true>> | |
: never | |
exist(): this | |
optional(): this extends BoxAlternativesSchema<infer B> | |
? BoxAlternativesSchema<BoxReq<B, false>> | |
: never | |
optional(): this | |
when< | |
R, | |
T1 extends mappedSchema, | |
T2 extends mappedSchema, | |
T extends { then: T1; otherwise: T2 } | |
>( | |
ref: R, | |
defs: T | |
): this extends BoxAlternativesSchema<infer O> | |
? O extends Box<infer oT, infer oR> | |
? BoxAlternativesSchema< | |
BoxType<O, oT | extractType<T['then']> | extractType<T['otherwise']>> | |
> | |
: BoxAlternativesSchema<Box<extractType<T['then']> | extractType<T['otherwise']>, false>> | |
: BoxAlternativesSchema<Box<extractType<T['then']> | extractType<T['otherwise']>, false>> | |
when(ref: string | joi.Reference, options: joi.WhenOptions): this | |
when(ref: joi.Schema, options: joi.WhenSchemaOptions): this | |
} | |
type maybeExtractBox<T> = T extends Box<infer O, infer R> ? O : T | |
type Required<T, K = keyof T> = { | |
[j in K extends keyof T | |
? T[K] extends BoxedPrimitive<infer B> | |
? B['R'] extends true | |
? K | |
: never | |
: never | |
: never]: true | |
} | |
type Optional<T, K = keyof T> = { | |
[j in K extends keyof T | |
? T[K] extends BoxedPrimitive<infer B> | |
? B['R'] extends false | |
? K | |
: never | |
: never | |
: never]: true | |
} | |
type extractMap<T extends mappedSchemaMap> = { [K in keyof Optional<T>]?: extractType<T[K]> } & | |
{ [K in keyof Required<T>]: extractType<T[K]> } | |
type extractOne<T extends mappedSchema> = | |
/** Primitive types */ | |
T extends primitiveType | |
? T | |
: /** Holds the extracted type */ | |
T extends BoxAnySchema<infer O> | |
? maybeExtractBox<O> | |
: T extends BoxBooleanSchema<infer O> | |
? maybeExtractBox<O> | |
: T extends BoxStringSchema<infer O> | |
? maybeExtractBox<O> | |
: T extends BoxNumberSchema<infer O> | |
? maybeExtractBox<O> | |
: T extends BoxDateSchema<infer O> | |
? maybeExtractBox<O> | |
: T extends BoxFunctionSchema<infer O> | |
? maybeExtractBox<O> | |
: T extends BoxArraySchema<infer O> | |
? maybeExtractBox<O>[] | |
: T extends BoxObjectSchema<infer O> | |
? maybeExtractBox<O> | |
: T extends BoxAlternativesSchema<infer O> | |
? maybeExtractBox<O> | |
: T extends joi.AnySchema | |
? any | |
: any | |
type extractType<T extends mappedSchema> = | |
/** | |
* Hack to support [Schema1, Schema2, ...N] alternatives notation | |
* Can't use extractType directly here because of cycles: | |
* ``` | |
* T extends Array<infer O> ? extractType<O> : | |
* ^ cycle | |
* ``` | |
*/ | |
T extends Array<infer O> | |
? O extends joi.SchemaLike | |
? extractOne<O> | |
: O extends BoxedPrimitive | |
? extractOne<O> | |
: O | |
: /** | |
* Handle Objects as schemas, without Joi.object at the root. | |
* It needs to come first than mappedSchema. | |
* It is difficult to avoid it to be inferred from extends clause. | |
*/ | |
T extends mappedSchemaMap | |
? extractMap<T> | |
: /** | |
* This is the base case for every schema implemented | |
*/ | |
T extends joi.SchemaLike | |
? extractOne<T> | |
: T extends BoxedPrimitive | |
? extractOne<T> | |
: /** | |
* Default case to handle primitives and schemas | |
*/ | |
extractOne<T> | |
} |
This works great @panzelva! FYI it does seem a little outdated. Any idea how much you actually edit from https://github.com/TCMiranda/joi-extract-type/blob/master/index.ts? The piece I just hit is the extend signature is wrong here, should be something like this now I believe:
extend(
extension: joi.Extension | joi.Extension[],
...extensions: Array<joi.Extension | joi.Extension[]>
): this
@kbrownlees Hello! Yes, this is gist is somewhat outdated - the reason is that in the end we moved away from Hapi (that was using Joi extensively) to Fastify and we lost reason for using Joi in the first place.
Also in other cases we started using Zod, that has extract-type functionality build in.
Feel free to adjust the gist as needed and if possible share your updated version to TCMiranda/joi-extract-type#22, I believe that other comunity members would appreciate it.
Here's a tweaked version that should solve a few issues. I'm no Joi expert so do with it what you will:
- Fixes Typescript compiler error on v4.9.4. (May also fix issue of the compiler crashing on versions 4.6 through 4.8 but I haven't confirmed)
- Fixes issue where
.default(value)
extracts to an optional type - Fixes optional type extraction
- Because of this,
.alternatives([...])
must also be fixed to excludeundefined
from the extracted types
- Because of this,
joi.d.ts
import joi from "joi";
declare module "joi" {
interface Root {
extend(...extensions: Array<joi.Extension | joi.ExtensionFactory>): this;
any<T>(): BoxAnySchema<Box<T, false>>;
string<T extends string>(): BoxStringSchema<Box<T, false>>;
number<T extends number>(): BoxNumberSchema<Box<T, false>>;
boolean<T extends boolean>(): BoxBooleanSchema<Box<T, false>>;
bool<T extends boolean>(): BoxBooleanSchema<Box<T, false>>;
date<T extends Date>(): BoxDateSchema<Box<T, false>>;
func<T extends Function>(): BoxFunctionSchema<Box<T, false>>;
array(): BoxArraySchema<Box<never, false>>;
object<T extends mappedSchemaMap>(schema?: T): BoxObjectSchema<Box<extractMap<T>, false>>;
alternatives<T extends mappedSchema[]>(
...alts: T
): BoxAlternativesSchema<Box<Exclude<extractType<typeof alts[number]>, undefined>, false>>;
alternatives<T extends mappedSchema[]>(
alts: T
): BoxAlternativesSchema<Box<Exclude<extractType<typeof alts[number]>, undefined>, false>>;
alt<T extends mappedSchema[]>(...alts: T): BoxAlternativesSchema<Box<extractType<typeof alts[number]>, false>>;
alt<T extends mappedSchema[]>(alts: T): BoxAlternativesSchema<Box<extractType<typeof alts[number]>, false>>;
}
/**
* Field requirements interface
*/
interface Box<T, R extends boolean> {
/** Type the schema holds */
T: T;
/** If this attribute is required when inside an object */
R: R;
}
// Operators
type BoxType<B extends BoxSchema, nT> = Box<nT, B["R"]>;
type BoxUnion<B extends BoxSchema, nT> = Box<B["T"] | nT, B["R"]>;
type BoxIntersection<B extends BoxSchema, nT> = Box<B["T"] & nT, B["R"]>;
type BoxReq<B extends BoxSchema, nR extends boolean> = Box<B["T"], nR>;
type BoxSchema = Box<any, boolean>;
type primitiveType = string | number | boolean | Function | Date | undefined | null | void;
type mappedSchema = joi.SchemaLike | BoxedPrimitive;
type mappedSchemaMap = { [K: string]: mappedSchema };
type extendsGuard<T, S> = S extends T ? S : T;
/**
* Every Schema that implements the Box to allow the extraction
*/
type BoxedPrimitive<T extends BoxSchema = any> =
| BoxAnySchema<T>
| BoxStringSchema<T>
| BoxNumberSchema<T>
| BoxBooleanSchema<T>
| BoxDateSchema<T>
| BoxFunctionSchema<T>
| BoxArraySchema<T>
| BoxObjectSchema<T>
| BoxAlternativesSchema<T>;
interface BoxAnySchema<N extends Box<any, boolean>> extends joi.AnySchema {
__schemaTypeLiteral: "BoxAnySchema";
default(value: any, description?: string): BoxAnySchema<BoxReq<N, true>>;
/** @deprecated */
default(): this;
allow<T>(...values: T[]): BoxAnySchema<BoxUnion<N, T>>;
allow<T>(values: T[]): BoxAnySchema<BoxUnion<N, T>>;
allow(...values: any[]): this;
allow(values: any[]): this;
valid<T>(...values: T[]): BoxAnySchema<BoxType<N, T>>;
valid<T>(values: T[]): BoxAnySchema<BoxType<N, T>>;
valid(...values: any[]): this;
valid(values: any[]): this;
required(): BoxAnySchema<BoxReq<N, true>>;
required(): this;
exist(): BoxAnySchema<BoxReq<N, true>>;
exist(): this;
optional(): BoxAnySchema<BoxReq<N, false>>;
optional(): this;
}
interface BoxStringSchema<N extends BoxSchema> extends joi.StringSchema {
__schemaTypeLiteral: "BoxStringSchema";
default(value: string, description?: string): BoxStringSchema<BoxReq<N, true>>;
/** @deprecated */
default(): this;
allow<T>(...values: T[]): BoxStringSchema<BoxUnion<N, T>>;
allow<T>(values: T[]): BoxStringSchema<BoxUnion<N, T>>;
allow(...values: any[]): this;
allow(values: any[]): this;
valid<T extends string>(...values: T[]): BoxStringSchema<BoxType<N, T>>;
valid<T extends string>(values: T[]): BoxStringSchema<BoxType<N, T>>;
valid(...values: any[]): this;
valid(values: any[]): this;
required(): BoxStringSchema<BoxReq<N, true>>;
required(): this;
exist(): BoxStringSchema<BoxReq<N, true>>;
exist(): this;
optional(): BoxStringSchema<BoxReq<N, false>>;
optional(): this;
}
interface BoxNumberSchema<N extends BoxSchema> extends joi.NumberSchema {
__schemaTypeLiteral: "BoxNumberSchema";
default(value: number, description?: string): BoxNumberSchema<BoxReq<N, true>>;
/** @deprecated */
default(): this;
allow<T>(...values: T[]): BoxNumberSchema<BoxUnion<N, T>>;
allow<T>(values: T[]): BoxNumberSchema<BoxUnion<N, T>>;
allow(...values: any[]): this;
allow(values: any[]): this;
valid<T extends string>(...values: T[]): BoxNumberSchema<BoxType<N, T>>;
valid<T extends string>(values: T[]): BoxNumberSchema<BoxType<N, T>>;
valid(...values: any[]): this;
valid(values: any[]): this;
required(): BoxNumberSchema<BoxReq<N, true>>;
required(): this;
exist(): BoxNumberSchema<BoxReq<N, true>>;
exist(): this;
optional(): BoxNumberSchema<BoxReq<N, false>>;
optional(): this;
}
interface BoxBooleanSchema<N extends BoxSchema> extends joi.BooleanSchema {
__schemaTypeLiteral: "BoxBooleanSchema";
default(value: boolean, description?: string): BoxBooleanSchema<BoxReq<N, true>>;
/** @deprecated */
default(): this;
allow<T>(...values: T[]): BoxBooleanSchema<BoxUnion<N, T>>;
allow<T>(values: T[]): BoxBooleanSchema<BoxUnion<N, T>>;
allow(...values: any[]): this;
allow(values: any[]): this;
valid<T extends string>(...values: T[]): BoxBooleanSchema<BoxType<N, T>>;
valid<T extends string>(values: T[]): BoxBooleanSchema<BoxType<N, T>>;
valid(...values: any[]): this;
valid(values: any[]): this;
required(): BoxBooleanSchema<BoxReq<N, true>>;
required(): this;
exist(): BoxBooleanSchema<BoxReq<N, true>>;
exist(): this;
optional(): BoxBooleanSchema<BoxReq<N, false>>;
optional(): this;
}
interface BoxDateSchema<N extends BoxSchema> extends joi.DateSchema {
__schemaTypeLiteral: "BoxDateSchema";
default(value: Date, description?: string): BoxDateSchema<BoxReq<N, true>>;
/** @deprecated */
default(): this;
allow<T>(...values: T[]): BoxDateSchema<BoxUnion<N, T>>;
allow<T>(values: T[]): BoxDateSchema<BoxUnion<N, T>>;
allow(...values: any[]): this;
allow(values: any[]): this;
valid<T extends string>(...values: T[]): BoxDateSchema<BoxType<N, T>>;
valid<T extends string>(values: T[]): BoxDateSchema<BoxType<N, T>>;
valid(...values: any[]): this;
valid(values: any[]): this;
required(): BoxDateSchema<BoxReq<N, true>>;
required(): this;
exist(): BoxDateSchema<BoxReq<N, true>>;
exist(): this;
optional(): BoxDateSchema<BoxReq<N, false>>;
optional(): this;
}
interface BoxFunctionSchema<N extends BoxSchema> extends joi.FunctionSchema {
__schemaTypeLiteral: "BoxFunctionSchema";
allow<T>(...values: T[]): BoxFunctionSchema<BoxUnion<N, T>>;
allow<T>(values: T[]): BoxFunctionSchema<BoxUnion<N, T>>;
allow(...values: any[]): this;
allow(values: any[]): this;
required(): BoxFunctionSchema<BoxReq<N, true>>;
required(): this;
exist(): BoxFunctionSchema<BoxReq<N, true>>;
exist(): this;
optional(): BoxFunctionSchema<BoxReq<N, false>>;
optional(): this;
}
interface BoxArraySchema<N extends BoxSchema> extends joi.ArraySchema {
__schemaTypeLiteral: "BoxArraySchema";
default(value: N["T"][], description?: string): BoxArraySchema<BoxReq<N, true>>;
/** @deprecated */
default(): this;
allow<T>(...values: T[]): BoxArraySchema<BoxUnion<N, T>>;
allow<T>(values: T[]): BoxArraySchema<BoxUnion<N, T>>;
allow(...values: any[]): this;
allow(values: any[]): this;
items<T extends mappedSchema>(type: T): BoxArraySchema<BoxUnion<N, extractType<T>>>;
items(...types: joi.SchemaLike[]): this;
items(types: joi.SchemaLike[]): this;
required(): BoxArraySchema<BoxReq<N, true>>;
required(): this;
exist(): BoxArraySchema<BoxReq<N, true>>;
exist(): this;
optional(): BoxArraySchema<BoxReq<N, false>>;
optional(): this;
}
interface BoxObjectSchema<N extends BoxSchema> extends joi.ObjectSchema {
__schemaTypeLiteral: "BoxObjectSchema";
default(value: N["T"], description?: string): BoxObjectSchema<BoxReq<N, true>>;
/** @deprecated */
default(): this;
allow<T>(...values: T[]): BoxObjectSchema<BoxUnion<N, T>>;
allow<T>(values: T[]): BoxObjectSchema<BoxUnion<N, T>>;
allow(...values: any[]): this;
allow(values: any[]): this;
keys<T extends mappedSchemaMap>(schema: T): BoxObjectSchema<BoxIntersection<N, extractMap<T>>>;
keys(schema?: joi.SchemaMap): this;
append<T extends mappedSchemaMap>(schema: T): BoxObjectSchema<BoxIntersection<N, extractMap<T>>>;
append(schema?: joi.SchemaMap): this;
pattern<S extends BoxStringSchema<any>, T extends mappedSchema>(
pattern: S,
schema: T
): BoxObjectSchema<BoxIntersection<N, extractMap<{ [key in extractType<S>]: T }>>>;
pattern<T extends mappedSchema>(
pattern: RegExp,
schema: T
): BoxObjectSchema<BoxIntersection<N, extractMap<{ [key: string]: T }>>>;
pattern(pattern: RegExp | joi.SchemaLike, schema: joi.SchemaLike): this;
required(): BoxObjectSchema<BoxReq<N, true>>;
required(): this;
exist(): BoxObjectSchema<BoxReq<N, true>>;
exist(): this;
optional(): BoxObjectSchema<BoxReq<N, false>>;
optional(): this;
}
interface BoxAlternativesSchema<N extends BoxSchema> extends joi.AlternativesSchema {
__schemaTypeLiteral: "BoxAlternativesSchema";
allow<T>(...values: T[]): BoxAlternativesSchema<BoxUnion<N, T>>;
allow<T>(values: T[]): BoxAlternativesSchema<BoxUnion<N, T>>;
allow(...values: any[]): this;
allow(values: any[]): this;
try<T extends mappedSchema[]>(
...values: T
): N extends Box<infer oT, infer oR>
? BoxAlternativesSchema<BoxType<N, oT | extractType<T>>>
: BoxAlternativesSchema<Box<extractType<T>, false>>;
try<T extends mappedSchema[]>(
values: T
): N extends Box<infer oT, infer oR>
? BoxAlternativesSchema<BoxType<N, oT | extractType<T>>>
: BoxAlternativesSchema<Box<extractType<T>, false>>;
try(...types: joi.SchemaLike[]): this;
try(types: joi.SchemaLike[]): this;
required(): BoxAlternativesSchema<BoxReq<N, true>>;
required(): this;
exist(): BoxAlternativesSchema<BoxReq<N, true>>;
exist(): this;
optional(): BoxAlternativesSchema<BoxReq<N, false>>;
optional(): this;
when<R, T1 extends mappedSchema, T2 extends mappedSchema, T extends { then: T1; otherwise: T2 }>(
ref: R,
defs: T
): N extends Box<infer oT, infer oR>
? BoxAlternativesSchema<BoxType<N, oT | extractType<T["then"]> | extractType<T["otherwise"]>>>
: BoxAlternativesSchema<Box<extractType<T["then"]> | extractType<T["otherwise"]>, false>>;
when(ref: string | joi.Reference, options: joi.WhenOptions): this;
when(ref: joi.Schema, options: joi.WhenSchemaOptions): this;
}
type maybeExtractBox<T> = T extends Box<infer O, infer R> ? (R extends true ? O : O | undefined) : T;
type Required<T, K = keyof T> = {
[j in K extends keyof T ? (T[K] extends BoxedPrimitive<infer B> ? (B["R"] extends true ? K : never) : never) : never]: true;
};
type Optional<T, K = keyof T> = {
[j in K extends keyof T ? (T[K] extends BoxedPrimitive<infer B> ? (B["R"] extends false ? K : never) : never) : never]: true;
};
type extractMap<T extends mappedSchemaMap> = { [K in keyof Optional<T>]?: extractType<T[K]> } & {
[K in keyof Required<T>]: extractType<T[K]>;
};
type extractOne<T extends mappedSchema> =
/** Primitive types */
T extends primitiveType
? T
: /** Holds the extracted type */
T extends BoxAnySchema<infer O>
? maybeExtractBox<O>
: T extends BoxBooleanSchema<infer O>
? maybeExtractBox<O>
: T extends BoxStringSchema<infer O>
? maybeExtractBox<O>
: T extends BoxNumberSchema<infer O>
? maybeExtractBox<O>
: T extends BoxDateSchema<infer O>
? maybeExtractBox<O>
: T extends BoxFunctionSchema<infer O>
? maybeExtractBox<O>
: T extends BoxArraySchema<infer O>
? maybeExtractBox<O>[]
: T extends BoxObjectSchema<infer O>
? maybeExtractBox<O>
: T extends BoxAlternativesSchema<infer O>
? maybeExtractBox<O>
: T extends joi.AnySchema
? any
: any;
type extractType<T extends mappedSchema> =
/**
* Hack to support [Schema1, Schema2, ...N] alternatives notation
* Can't use extractType directly here because of cycles:
* ```
* T extends Array<infer O> ? extractType<O> :
* ^ cycle
* ```
*/
T extends Array<infer O>
? O extends joi.SchemaLike
? extractOne<O>
: O extends BoxedPrimitive
? extractOne<O>
: O
: /**
* Handle Objects as schemas, without Joi.object at the root.
* It needs to come first than mappedSchema.
* It is difficult to avoid it to be inferred from extends clause.
*/
T extends mappedSchemaMap
? extractMap<T>
: /**
* This is the base case for every schema implemented
*/
T extends joi.SchemaLike
? extractOne<T>
: T extends BoxedPrimitive
? extractOne<T>
: /**
* Default case to handle primitives and schemas
*/
extractOne<T>;
}
Here's a tweaked version that should solve a few issues. I'm no Joi expert so do with it what you will:
Fixes Typescript compiler error on v4.9.4. (May also fix issue of the compiler crashing on versions 4.6 through 4.8 but I haven't confirmed)
Fixes issue where
.default(value)
extracts to an optional typeFixes optional type extraction
- Because of this,
.alternatives([...])
must also be fixed to excludeundefined
from the extracted typesjoi.d.ts
import joi from "joi"; declare module "joi" { interface Root { extend(...extensions: Array<joi.Extension | joi.ExtensionFactory>): this; any<T>(): BoxAnySchema<Box<T, false>>; string<T extends string>(): BoxStringSchema<Box<T, false>>; number<T extends number>(): BoxNumberSchema<Box<T, false>>; boolean<T extends boolean>(): BoxBooleanSchema<Box<T, false>>; bool<T extends boolean>(): BoxBooleanSchema<Box<T, false>>; date<T extends Date>(): BoxDateSchema<Box<T, false>>; func<T extends Function>(): BoxFunctionSchema<Box<T, false>>; array(): BoxArraySchema<Box<never, false>>; object<T extends mappedSchemaMap>(schema?: T): BoxObjectSchema<Box<extractMap<T>, false>>; alternatives<T extends mappedSchema[]>( ...alts: T ): BoxAlternativesSchema<Box<Exclude<extractType<typeof alts[number]>, undefined>, false>>; alternatives<T extends mappedSchema[]>( alts: T ): BoxAlternativesSchema<Box<Exclude<extractType<typeof alts[number]>, undefined>, false>>; alt<T extends mappedSchema[]>(...alts: T): BoxAlternativesSchema<Box<extractType<typeof alts[number]>, false>>; alt<T extends mappedSchema[]>(alts: T): BoxAlternativesSchema<Box<extractType<typeof alts[number]>, false>>; } /** * Field requirements interface */ interface Box<T, R extends boolean> { /** Type the schema holds */ T: T; /** If this attribute is required when inside an object */ R: R; } // Operators type BoxType<B extends BoxSchema, nT> = Box<nT, B["R"]>; type BoxUnion<B extends BoxSchema, nT> = Box<B["T"] | nT, B["R"]>; type BoxIntersection<B extends BoxSchema, nT> = Box<B["T"] & nT, B["R"]>; type BoxReq<B extends BoxSchema, nR extends boolean> = Box<B["T"], nR>; type BoxSchema = Box<any, boolean>; type primitiveType = string | number | boolean | Function | Date | undefined | null | void; type mappedSchema = joi.SchemaLike | BoxedPrimitive; type mappedSchemaMap = { [K: string]: mappedSchema }; type extendsGuard<T, S> = S extends T ? S : T; /** * Every Schema that implements the Box to allow the extraction */ type BoxedPrimitive<T extends BoxSchema = any> = | BoxAnySchema<T> | BoxStringSchema<T> | BoxNumberSchema<T> | BoxBooleanSchema<T> | BoxDateSchema<T> | BoxFunctionSchema<T> | BoxArraySchema<T> | BoxObjectSchema<T> | BoxAlternativesSchema<T>; interface BoxAnySchema<N extends Box<any, boolean>> extends joi.AnySchema { __schemaTypeLiteral: "BoxAnySchema"; default(value: any, description?: string): BoxAnySchema<BoxReq<N, true>>; /** @deprecated */ default(): this; allow<T>(...values: T[]): BoxAnySchema<BoxUnion<N, T>>; allow<T>(values: T[]): BoxAnySchema<BoxUnion<N, T>>; allow(...values: any[]): this; allow(values: any[]): this; valid<T>(...values: T[]): BoxAnySchema<BoxType<N, T>>; valid<T>(values: T[]): BoxAnySchema<BoxType<N, T>>; valid(...values: any[]): this; valid(values: any[]): this; required(): BoxAnySchema<BoxReq<N, true>>; required(): this; exist(): BoxAnySchema<BoxReq<N, true>>; exist(): this; optional(): BoxAnySchema<BoxReq<N, false>>; optional(): this; } interface BoxStringSchema<N extends BoxSchema> extends joi.StringSchema { __schemaTypeLiteral: "BoxStringSchema"; default(value: string, description?: string): BoxStringSchema<BoxReq<N, true>>; /** @deprecated */ default(): this; allow<T>(...values: T[]): BoxStringSchema<BoxUnion<N, T>>; allow<T>(values: T[]): BoxStringSchema<BoxUnion<N, T>>; allow(...values: any[]): this; allow(values: any[]): this; valid<T extends string>(...values: T[]): BoxStringSchema<BoxType<N, T>>; valid<T extends string>(values: T[]): BoxStringSchema<BoxType<N, T>>; valid(...values: any[]): this; valid(values: any[]): this; required(): BoxStringSchema<BoxReq<N, true>>; required(): this; exist(): BoxStringSchema<BoxReq<N, true>>; exist(): this; optional(): BoxStringSchema<BoxReq<N, false>>; optional(): this; } interface BoxNumberSchema<N extends BoxSchema> extends joi.NumberSchema { __schemaTypeLiteral: "BoxNumberSchema"; default(value: number, description?: string): BoxNumberSchema<BoxReq<N, true>>; /** @deprecated */ default(): this; allow<T>(...values: T[]): BoxNumberSchema<BoxUnion<N, T>>; allow<T>(values: T[]): BoxNumberSchema<BoxUnion<N, T>>; allow(...values: any[]): this; allow(values: any[]): this; valid<T extends string>(...values: T[]): BoxNumberSchema<BoxType<N, T>>; valid<T extends string>(values: T[]): BoxNumberSchema<BoxType<N, T>>; valid(...values: any[]): this; valid(values: any[]): this; required(): BoxNumberSchema<BoxReq<N, true>>; required(): this; exist(): BoxNumberSchema<BoxReq<N, true>>; exist(): this; optional(): BoxNumberSchema<BoxReq<N, false>>; optional(): this; } interface BoxBooleanSchema<N extends BoxSchema> extends joi.BooleanSchema { __schemaTypeLiteral: "BoxBooleanSchema"; default(value: boolean, description?: string): BoxBooleanSchema<BoxReq<N, true>>; /** @deprecated */ default(): this; allow<T>(...values: T[]): BoxBooleanSchema<BoxUnion<N, T>>; allow<T>(values: T[]): BoxBooleanSchema<BoxUnion<N, T>>; allow(...values: any[]): this; allow(values: any[]): this; valid<T extends string>(...values: T[]): BoxBooleanSchema<BoxType<N, T>>; valid<T extends string>(values: T[]): BoxBooleanSchema<BoxType<N, T>>; valid(...values: any[]): this; valid(values: any[]): this; required(): BoxBooleanSchema<BoxReq<N, true>>; required(): this; exist(): BoxBooleanSchema<BoxReq<N, true>>; exist(): this; optional(): BoxBooleanSchema<BoxReq<N, false>>; optional(): this; } interface BoxDateSchema<N extends BoxSchema> extends joi.DateSchema { __schemaTypeLiteral: "BoxDateSchema"; default(value: Date, description?: string): BoxDateSchema<BoxReq<N, true>>; /** @deprecated */ default(): this; allow<T>(...values: T[]): BoxDateSchema<BoxUnion<N, T>>; allow<T>(values: T[]): BoxDateSchema<BoxUnion<N, T>>; allow(...values: any[]): this; allow(values: any[]): this; valid<T extends string>(...values: T[]): BoxDateSchema<BoxType<N, T>>; valid<T extends string>(values: T[]): BoxDateSchema<BoxType<N, T>>; valid(...values: any[]): this; valid(values: any[]): this; required(): BoxDateSchema<BoxReq<N, true>>; required(): this; exist(): BoxDateSchema<BoxReq<N, true>>; exist(): this; optional(): BoxDateSchema<BoxReq<N, false>>; optional(): this; } interface BoxFunctionSchema<N extends BoxSchema> extends joi.FunctionSchema { __schemaTypeLiteral: "BoxFunctionSchema"; allow<T>(...values: T[]): BoxFunctionSchema<BoxUnion<N, T>>; allow<T>(values: T[]): BoxFunctionSchema<BoxUnion<N, T>>; allow(...values: any[]): this; allow(values: any[]): this; required(): BoxFunctionSchema<BoxReq<N, true>>; required(): this; exist(): BoxFunctionSchema<BoxReq<N, true>>; exist(): this; optional(): BoxFunctionSchema<BoxReq<N, false>>; optional(): this; } interface BoxArraySchema<N extends BoxSchema> extends joi.ArraySchema { __schemaTypeLiteral: "BoxArraySchema"; default(value: N["T"][], description?: string): BoxArraySchema<BoxReq<N, true>>; /** @deprecated */ default(): this; allow<T>(...values: T[]): BoxArraySchema<BoxUnion<N, T>>; allow<T>(values: T[]): BoxArraySchema<BoxUnion<N, T>>; allow(...values: any[]): this; allow(values: any[]): this; items<T extends mappedSchema>(type: T): BoxArraySchema<BoxUnion<N, extractType<T>>>; items(...types: joi.SchemaLike[]): this; items(types: joi.SchemaLike[]): this; required(): BoxArraySchema<BoxReq<N, true>>; required(): this; exist(): BoxArraySchema<BoxReq<N, true>>; exist(): this; optional(): BoxArraySchema<BoxReq<N, false>>; optional(): this; } interface BoxObjectSchema<N extends BoxSchema> extends joi.ObjectSchema { __schemaTypeLiteral: "BoxObjectSchema"; default(value: N["T"], description?: string): BoxObjectSchema<BoxReq<N, true>>; /** @deprecated */ default(): this; allow<T>(...values: T[]): BoxObjectSchema<BoxUnion<N, T>>; allow<T>(values: T[]): BoxObjectSchema<BoxUnion<N, T>>; allow(...values: any[]): this; allow(values: any[]): this; keys<T extends mappedSchemaMap>(schema: T): BoxObjectSchema<BoxIntersection<N, extractMap<T>>>; keys(schema?: joi.SchemaMap): this; append<T extends mappedSchemaMap>(schema: T): BoxObjectSchema<BoxIntersection<N, extractMap<T>>>; append(schema?: joi.SchemaMap): this; pattern<S extends BoxStringSchema<any>, T extends mappedSchema>( pattern: S, schema: T ): BoxObjectSchema<BoxIntersection<N, extractMap<{ [key in extractType<S>]: T }>>>; pattern<T extends mappedSchema>( pattern: RegExp, schema: T ): BoxObjectSchema<BoxIntersection<N, extractMap<{ [key: string]: T }>>>; pattern(pattern: RegExp | joi.SchemaLike, schema: joi.SchemaLike): this; required(): BoxObjectSchema<BoxReq<N, true>>; required(): this; exist(): BoxObjectSchema<BoxReq<N, true>>; exist(): this; optional(): BoxObjectSchema<BoxReq<N, false>>; optional(): this; } interface BoxAlternativesSchema<N extends BoxSchema> extends joi.AlternativesSchema { __schemaTypeLiteral: "BoxAlternativesSchema"; allow<T>(...values: T[]): BoxAlternativesSchema<BoxUnion<N, T>>; allow<T>(values: T[]): BoxAlternativesSchema<BoxUnion<N, T>>; allow(...values: any[]): this; allow(values: any[]): this; try<T extends mappedSchema[]>( ...values: T ): N extends Box<infer oT, infer oR> ? BoxAlternativesSchema<BoxType<N, oT | extractType<T>>> : BoxAlternativesSchema<Box<extractType<T>, false>>; try<T extends mappedSchema[]>( values: T ): N extends Box<infer oT, infer oR> ? BoxAlternativesSchema<BoxType<N, oT | extractType<T>>> : BoxAlternativesSchema<Box<extractType<T>, false>>; try(...types: joi.SchemaLike[]): this; try(types: joi.SchemaLike[]): this; required(): BoxAlternativesSchema<BoxReq<N, true>>; required(): this; exist(): BoxAlternativesSchema<BoxReq<N, true>>; exist(): this; optional(): BoxAlternativesSchema<BoxReq<N, false>>; optional(): this; when<R, T1 extends mappedSchema, T2 extends mappedSchema, T extends { then: T1; otherwise: T2 }>( ref: R, defs: T ): N extends Box<infer oT, infer oR> ? BoxAlternativesSchema<BoxType<N, oT | extractType<T["then"]> | extractType<T["otherwise"]>>> : BoxAlternativesSchema<Box<extractType<T["then"]> | extractType<T["otherwise"]>, false>>; when(ref: string | joi.Reference, options: joi.WhenOptions): this; when(ref: joi.Schema, options: joi.WhenSchemaOptions): this; } type maybeExtractBox<T> = T extends Box<infer O, infer R> ? (R extends true ? O : O | undefined) : T; type Required<T, K = keyof T> = { [j in K extends keyof T ? (T[K] extends BoxedPrimitive<infer B> ? (B["R"] extends true ? K : never) : never) : never]: true; }; type Optional<T, K = keyof T> = { [j in K extends keyof T ? (T[K] extends BoxedPrimitive<infer B> ? (B["R"] extends false ? K : never) : never) : never]: true; }; type extractMap<T extends mappedSchemaMap> = { [K in keyof Optional<T>]?: extractType<T[K]> } & { [K in keyof Required<T>]: extractType<T[K]>; }; type extractOne<T extends mappedSchema> = /** Primitive types */ T extends primitiveType ? T : /** Holds the extracted type */ T extends BoxAnySchema<infer O> ? maybeExtractBox<O> : T extends BoxBooleanSchema<infer O> ? maybeExtractBox<O> : T extends BoxStringSchema<infer O> ? maybeExtractBox<O> : T extends BoxNumberSchema<infer O> ? maybeExtractBox<O> : T extends BoxDateSchema<infer O> ? maybeExtractBox<O> : T extends BoxFunctionSchema<infer O> ? maybeExtractBox<O> : T extends BoxArraySchema<infer O> ? maybeExtractBox<O>[] : T extends BoxObjectSchema<infer O> ? maybeExtractBox<O> : T extends BoxAlternativesSchema<infer O> ? maybeExtractBox<O> : T extends joi.AnySchema ? any : any; type extractType<T extends mappedSchema> = /** * Hack to support [Schema1, Schema2, ...N] alternatives notation * Can't use extractType directly here because of cycles: * ``` * T extends Array<infer O> ? extractType<O> : * ^ cycle * ``` */ T extends Array<infer O> ? O extends joi.SchemaLike ? extractOne<O> : O extends BoxedPrimitive ? extractOne<O> : O : /** * Handle Objects as schemas, without Joi.object at the root. * It needs to come first than mappedSchema. * It is difficult to avoid it to be inferred from extends clause. */ T extends mappedSchemaMap ? extractMap<T> : /** * This is the base case for every schema implemented */ T extends joi.SchemaLike ? extractOne<T> : T extends BoxedPrimitive ? extractOne<T> : /** * Default case to handle primitives and schemas */ extractOne<T>; }
How can this be used in project upgrading from @hapi/joi to new [email protected]? Do I manually overwrite index.d.ts in node_modules/joi-extract-typedist/index.d.ts with this file contents?
I'm getting these errors on Typescript 4.1.2
Pretty much the same circular reference error throughout the file, any idea?