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: Type Errors and Context as NonEmptyArray (#670) #671

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
6 changes: 3 additions & 3 deletions docs/modules/index.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ Added in v1.0.0
**Signature**

```ts
export interface Context extends ReadonlyArray<ContextEntry> {}
export interface Context extends ReadonlyNonEmptyArray<ContextEntry> {}
```

Added in v1.0.0
Expand All @@ -399,7 +399,7 @@ Added in v1.0.0
**Signature**

```ts
export interface Errors extends Array<ValidationError> {}
export interface Errors extends NonEmptyArray<ValidationError> {}
```

Added in v1.0.0
Expand Down Expand Up @@ -2123,7 +2123,7 @@ Added in v1.0.0
**Signature**

```ts
export declare function appendContext(c: Context, key: string, decoder: Decoder<any, any>, actual?: unknown): Context
export declare const appendContext: (c: Context, key: string, decoder: Decoder<any, any>, actual?: unknown) => Context
```

Added in v1.0.0
Expand Down
49 changes: 22 additions & 27 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
*/
import { Either, isLeft, left, right } from 'fp-ts/lib/Either'
import { Predicate, Refinement } from 'fp-ts/lib/function'
import { ReadonlyNonEmptyArray } from 'fp-ts/lib/ReadonlyNonEmptyArray'
import { readonlyNonEmptyArray } from 'fp-ts'

// -------------------------------------------------------------------------------------
// Decode error
Expand All @@ -23,7 +25,7 @@ export interface ContextEntry {
* @category Decode error
* @since 1.0.0
*/
export interface Context extends ReadonlyArray<ContextEntry> {}
export interface Context extends ReadonlyNonEmptyArray<ContextEntry> {}

/**
* @category Decode error
Expand All @@ -42,7 +44,7 @@ export interface ValidationError {
* @category Decode error
* @since 1.0.0
*/
export interface Errors extends Array<ValidationError> {}
export interface Errors extends NonEmptyArray<ValidationError> {}

/**
* @category Decode error
Expand Down Expand Up @@ -237,15 +239,8 @@ export function getContextEntry(key: string, decoder: Decoder<any, any>): Contex
/**
* @since 1.0.0
*/
export function appendContext(c: Context, key: string, decoder: Decoder<any, any>, actual?: unknown): Context {
const len = c.length
const r = Array(len + 1)
for (let i = 0; i < len; i++) {
r[i] = c[i]
}
r[len] = { key, type: decoder, actual }
return r
}
export const appendContext = (c: Context, key: string, decoder: Decoder<any, any>, actual?: unknown): Context =>
readonlyNonEmptyArray.snoc(c, { key, type: decoder, actual })

function pushAll<A>(xs: Array<A>, ys: Array<A>): void {
const l = ys.length
Expand Down Expand Up @@ -340,7 +335,7 @@ function enumerableRecord<D extends Mixed, C extends Mixed>(
}
const o = e.right
const a: { [key: string]: any } = {}
const errors: Errors = []
const errors: ValidationError[] = []
let changed = false
for (let i = 0; i < len; i++) {
const k = keys[i]
Expand All @@ -354,7 +349,7 @@ function enumerableRecord<D extends Mixed, C extends Mixed>(
a[k] = vok
}
}
return errors.length > 0 ? failures(errors) : success((changed || Object.keys(o).length !== len ? a : o) as any)
return isNonEmpty(errors) ? failures(errors) : success((changed || Object.keys(o).length !== len ? a : o) as any)
},
codomain.encode === identity
? identity
Expand Down Expand Up @@ -405,7 +400,7 @@ function nonEnumerableRecord<D extends Mixed, C extends Mixed>(
(u, c) => {
if (UnknownRecord.is(u)) {
const a: { [key: string]: any } = {}
const errors: Errors = []
const errors: ValidationError[] = []
const keys = Object.keys(u)
const len = keys.length
let changed = false
Expand All @@ -429,7 +424,7 @@ function nonEnumerableRecord<D extends Mixed, C extends Mixed>(
}
}
}
return errors.length > 0 ? failures(errors) : success((changed ? a : u) as any)
return isNonEmpty(errors) ? failures(errors) : success((changed ? a : u) as any)
}
if (isAnyC(codomain) && Array.isArray(u)) {
return success(u)
Expand Down Expand Up @@ -1278,7 +1273,7 @@ export function array<C extends Mixed>(item: C, name = `Array<${item.name}>`): A
const us = e.right
const len = us.length
let as: Array<TypeOf<C>> = us
const errors: Errors = []
const errors: ValidationError[] = []
for (let i = 0; i < len; i++) {
const ui = us[i]
const result = item.validate(ui, appendContext(c, String(i), item, ui))
Expand All @@ -1294,7 +1289,7 @@ export function array<C extends Mixed>(item: C, name = `Array<${item.name}>`): A
}
}
}
return errors.length > 0 ? failures(errors) : success(as)
return isNonEmpty(errors) ? failures(errors) : success(as)
},
item.encode === identity ? identity : (a) => a.map(item.encode),
item
Expand Down Expand Up @@ -1356,7 +1351,7 @@ export function type<P extends Props>(props: P, name: string = getInterfaceTypeN
}
const o = e.right
let a = o
const errors: Errors = []
const errors: ValidationError[] = []
for (let i = 0; i < len; i++) {
const k = keys[i]
const ak = a[k]
Expand All @@ -1375,7 +1370,7 @@ export function type<P extends Props>(props: P, name: string = getInterfaceTypeN
}
}
}
return errors.length > 0 ? failures(errors) : success(a as any)
return isNonEmpty(errors) ? failures(errors) : success(a as any)
},
useIdentity(types)
? identity
Expand Down Expand Up @@ -1452,7 +1447,7 @@ export function partial<P extends Props>(
}
const o = e.right
let a = o
const errors: Errors = []
const errors: ValidationError[] = []
for (let i = 0; i < len; i++) {
const k = keys[i]
const ak = a[k]
Expand All @@ -1473,7 +1468,7 @@ export function partial<P extends Props>(
}
}
}
return errors.length > 0 ? failures(errors) : success(a as any)
return isNonEmpty(errors) ? failures(errors) : success(a as any)
},
useIdentity(types)
? identity
Expand Down Expand Up @@ -1616,7 +1611,7 @@ export function union<CS extends [Mixed, Mixed, ...Array<Mixed>]>(
name,
(u): u is TypeOf<CS[number]> => codecs.some((type) => type.is(u)),
(u, c) => {
const errors: Errors = []
const errors: ValidationError[] = []
for (let i = 0; i < codecs.length; i++) {
const codec = codecs[i]
const result = codec.validate(u, appendContext(c, String(i), codec, u))
Expand All @@ -1626,7 +1621,7 @@ export function union<CS extends [Mixed, Mixed, ...Array<Mixed>]>(
return success(result.right)
}
}
return failures(errors)
return failures(errors as Errors)
},
useIdentity(codecs)
? identity
Expand Down Expand Up @@ -1719,7 +1714,7 @@ export function intersection<CS extends [Mixed, Mixed, ...Array<Mixed>]>(
? success
: (u, c) => {
const us: Array<unknown> = []
const errors: Errors = []
const errors: ValidationError[] = []
for (let i = 0; i < len; i++) {
const codec = codecs[i]
const result = codec.validate(u, appendContext(c, String(i), codec, u))
Expand All @@ -1729,7 +1724,7 @@ export function intersection<CS extends [Mixed, Mixed, ...Array<Mixed>]>(
us.push(result.right)
}
}
return errors.length > 0 ? failures(errors) : success(mergeAll(u, us))
return isNonEmpty(errors) ? failures(errors) : success(mergeAll(u, us))
},
codecs.length === 0
? identity
Expand Down Expand Up @@ -1825,7 +1820,7 @@ export function tuple<CS extends [Mixed, ...Array<Mixed>]>(
}
const us = e.right
let as: Array<any> = us.length > len ? us.slice(0, len) : us // strip additional components
const errors: Errors = []
const errors: ValidationError[] = []
for (let i = 0; i < len; i++) {
const a = us[i]
const type = codecs[i]
Expand All @@ -1843,7 +1838,7 @@ export function tuple<CS extends [Mixed, ...Array<Mixed>]>(
}
}
}
return errors.length > 0 ? failures(errors) : success(as)
return isNonEmpty(errors) ? failures(errors) : success(as)
},
useIdentity(codecs) ? identity : (a) => codecs.map((type, i) => type.encode(a[i])),
codecs
Expand Down