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

Add toJSON to TypeScript typings #166

Merged
merged 6 commits into from Aug 10, 2018
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
74 changes: 40 additions & 34 deletions index.d.ts
@@ -1,3 +1,6 @@
type AnySchema = NullSchema | BooleanSchema | NumberSchema | StringSchema | AnyEnumSchema | AnyArraySchema | AnyObjectSchema
type StringKeys<T> = (keyof T) & string

interface NullSchema {
type: 'null'
}
Expand All @@ -14,50 +17,42 @@ interface StringSchema {
type: 'string'
}

type LeafSchema = NullSchema | BooleanSchema | NumberSchema | StringSchema

interface EnumSchema<T> {
enum: T[]
interface AnyEnumSchema extends EnumSchema<any> {}
interface EnumSchema<Enum> {
enum: Enum[]
}

interface ArraySchema<T extends LeafSchema | EnumSchema<any> | ArraySchema<LeafSchema | EnumSchema<any> | ObjectSchema<ObjectProps, any>> | ObjectSchema<ObjectProps, any>> {
interface AnyArraySchema extends ArraySchema<AnySchema> {}
interface ArraySchema<ItemSchema extends AnySchema> {
type: 'array'
items: T
items: ItemSchema
}

type ObjectProps = { [K in string]: LeafSchema | EnumSchema<any> | ArraySchema<LeafSchema | EnumSchema<any> | ArraySchema<LeafSchema | EnumSchema<any> | ObjectSchema<ObjectProps, any>> | ObjectSchema<ObjectProps, any>> | ObjectSchema<ObjectProps, any> }

interface ObjectSchema<T extends ObjectProps, R extends keyof T> {
interface AnyObjectSchema extends ObjectSchema<Record<string, AnySchema>, string> {}
interface ObjectSchema<Properties extends Record<string, AnySchema>, Required extends StringKeys<Properties>> {
additionalProperties?: boolean
type: 'object'
properties: T
required: R[]
properties: Properties
required: Required[]
}

interface ExtractedSchemaArray<T> extends Array<ExtractSchemaType<T>> {}
interface ArrayFromSchema<ItemSchema extends AnySchema> extends Array<TypeFromSchema<ItemSchema>> {}

declare type ExtractedSchemaObject<T, R> = {
[K in keyof T]: (K extends R ? ExtractSchemaType<T[K]> : ExtractSchemaType<T[K]> | undefined)
type ObjectFromSchema<Properties extends Record<string, AnySchema>, Required extends StringKeys<Properties>> = {
[Key in keyof Properties]: (Key extends Required ? TypeFromSchema<Properties[Key]> : TypeFromSchema<Properties[Key]> | undefined)
}

declare type ExtractSchemaType<Type> = (
Type extends EnumSchema<infer T> ? T
: Type extends NullSchema ? null
: Type extends BooleanSchema ? boolean
: Type extends NumberSchema ? number
: Type extends StringSchema ? string
: Type extends ArraySchema<infer T> ? ExtractedSchemaArray<T>
: Type extends ObjectSchema<infer T, infer R> ? ExtractedSchemaObject<T, R>
type TypeFromSchema<Schema extends AnySchema> = (
Schema extends EnumSchema<infer Enum> ? Enum
: Schema extends NullSchema ? null
: Schema extends BooleanSchema ? boolean
: Schema extends NumberSchema ? number
: Schema extends StringSchema ? string
: Schema extends ArraySchema<infer ItemSchema> ? ArrayFromSchema<ItemSchema>
: Schema extends ObjectSchema<infer Properties, infer Required> ? ObjectFromSchema<Properties, Required>
: never
)

declare type GenericSchema = (
{ enum: any[] } |
{ type: 'string' | 'number' | 'boolean' | 'null' } |
{ type: 'array', items: GenericSchema } |
{ type: 'object', properties: ObjectProps }
)

declare namespace factory {
interface ValidationError {
field: string
Expand All @@ -67,13 +62,24 @@ declare namespace factory {
}
}

declare function createValidator<T extends ObjectProps, R extends keyof T> (schema: ObjectSchema<T, R>, options?: any): ((input: unknown, options?: any) => input is { [K in keyof T]: (K extends R ? ExtractSchemaType<T[K]> : ExtractSchemaType<T[K]> | undefined) }) & { errors: factory.ValidationError[] }
declare function createValidator<T extends GenericSchema> (schema: T, options?: any): ((input: unknown, options?: any) => input is ExtractSchemaType<T>) & { errors: factory.ValidationError[] }
interface Validator<Schema extends AnySchema, Output = TypeFromSchema<Schema>> {
(input: unknown, options?: any): input is Output
errors: factory.ValidationError[]
toJSON(): Schema
}

interface Filter<Output> {
(input: Output, options?: any): Output
}

declare function createFilter<T extends ObjectProps, R extends keyof T> (schema: ObjectSchema<T, R>, options?: any): ((input: { [K in keyof T]: (K extends R ? ExtractSchemaType<T[K]> : ExtractSchemaType<T[K]> | undefined) }, options?: any) => { [K in keyof T]: (K extends R ? ExtractSchemaType<T[K]> : ExtractSchemaType<T[K]> | undefined) })
declare function createFilter<T extends GenericSchema> (schema: T, options?: any): ((input: ExtractSchemaType<T>, options?: any) => ExtractSchemaType<T>)
interface Factory {
<Properties extends Record<string, AnySchema>, Required extends StringKeys<Properties>> (schema: ObjectSchema<Properties, Required>, options?: any): Validator<ObjectSchema<Properties, Required>>
<Schema extends AnySchema> (schema: Schema, options?: any): Validator<Schema>

createFilter<Properties extends Record<string, AnySchema>, Required extends StringKeys<Properties>> (schema: ObjectSchema<Properties, Required>, options?: any): Filter<ObjectFromSchema<Properties, Required>>
createFilter<Schema extends AnySchema> (schema: Schema, options?: any): Filter<TypeFromSchema<Schema>>
}

declare type Factory = (typeof createValidator) & { filter: typeof createFilter }
declare const factory: Factory

export = factory
3 changes: 3 additions & 0 deletions test/typings.ts
Expand Up @@ -9,6 +9,7 @@ function assertType<T>(value: T): void {}
const input = null as unknown

const nullValidator = createValidator({ type: 'null' })
assertType<{ type: 'null' }>(nullValidator.toJSON())

if (nullValidator(input)) {
assertType<null>(input)
Expand All @@ -22,12 +23,14 @@ assertType<string>(nullValidator.errors[0].type)
assertType<unknown>(nullValidator.errors[0].value)

const numberValidator = createValidator({ type: 'number' })
assertType<{ type: 'number' }>(numberValidator.toJSON())

if (numberValidator(input)) {
assertType<number>(input)
}

const stringValidator = createValidator({ type: 'string' })
assertType<{ type: 'string' }>(stringValidator.toJSON())

if (stringValidator(input)) {
assertType<string>(input)
Expand Down