Skip to content

Commit

Permalink
Copy all doc strings to TypeScript too + some side fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
nvie committed Feb 7, 2022
1 parent fe60bf6 commit 4dfbbe2
Show file tree
Hide file tree
Showing 14 changed files with 408 additions and 23 deletions.
18 changes: 9 additions & 9 deletions docs/Decoder.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ for (name, info) in DECODER_METHODS.items():
]]]-->
---

<a name="verify" href="#verify">#</a> **.verify**(blob: <i style="color: #267f99">mixed</i>): <i style="color: #267f99">T</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L102-L114 'Source')
<a name="verify" href="#verify">#</a> **.verify**(blob: <i style="color: #267f99">mixed</i>): <i style="color: #267f99">T</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L138-L150 'Source')
{: .signature}

Verifies the untrusted/unknown input and either accepts or rejects it.
Expand All @@ -89,7 +89,7 @@ number.verify('hello'); // throws

---

<a name="value" href="#value">#</a> **.value**(blob: <i style="color: #267f99">mixed</i>): <i style="color: #267f99">T | undefined</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L116-L126 'Source')
<a name="value" href="#value">#</a> **.value**(blob: <i style="color: #267f99">mixed</i>): <i style="color: #267f99">T | undefined</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L152-L162 'Source')
{: .signature}

Verifies the untrusted/unknown input and either accepts or rejects it.
Expand All @@ -115,7 +115,7 @@ string.value(42); // undefined
---

<a name="decode" href="#decode">#</a> **.decode**(blob: <i style="color: #267f99">mixed</i>): <i style="color: #267f99">DecodeResult&lt;T&gt;</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L90-L100 'Source')
<a name="decode" href="#decode">#</a> **.decode**(blob: <i style="color: #267f99">mixed</i>): <i style="color: #267f99">DecodeResult&lt;T&gt;</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L126-L136 'Source')
{: .signature}

Verifies the untrusted/unknown input and either accepts or rejects it.
Expand All @@ -137,7 +137,7 @@ number.decode('hi'); // { ok: false, error: { type: 'scalar', value: 'hi', text

---

<a name="transform" href="#transform">#</a> **.transform**&lt;<i style="color: #267f99">V</i>&gt;(transformFn: <i style="color: #267f99">(T) =&gt; V</i>): <i style="color: #267f99"><a href="/Decoder.html" style="color: inherit">Decoder</a>&lt;V&gt;</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L128-L136 'Source')
<a name="transform" href="#transform">#</a> **.transform**&lt;<i style="color: #267f99">V</i>&gt;(transformFn: <i style="color: #267f99">(T) =&gt; V</i>): <i style="color: #267f99"><a href="/Decoder.html" style="color: inherit">Decoder</a>&lt;V&gt;</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L164-L172 'Source')
{: .signature}

Accepts any value the given decoder accepts, and on success, will call
Expand All @@ -157,7 +157,7 @@ upper.verify(4); // throws

---

<a name="refine" href="#refine">#</a> **.refine**(predicate: <i style="color: #267f99">T =&gt; boolean</i>, message: <i style="color: #267f99">string</i>): <i style="color: #267f99"><a href="/Decoder.html" style="color: inherit">Decoder</a>&lt;T&gt;</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L138-L151 'Source')
<a name="refine" href="#refine">#</a> **.refine**(predicate: <i style="color: #267f99">T =&gt; boolean</i>, message: <i style="color: #267f99">string</i>): <i style="color: #267f99"><a href="/Decoder.html" style="color: inherit">Decoder</a>&lt;T&gt;</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L174-L187 'Source')
{: .signature}

Adds an extra predicate to a decoder. The new decoder is like the
Expand All @@ -182,7 +182,7 @@ In TypeScript, if you provide a predicate that also is a [type predicate](https:

---

<a name="reject" href="#reject">#</a> **.reject**(rejectFn: <i style="color: #267f99">T =&gt; string | null</i>): <i style="color: #267f99"><a href="/Decoder.html" style="color: inherit">Decoder</a>&lt;T&gt;</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L179-L197 'Source')
<a name="reject" href="#reject">#</a> **.reject**(rejectFn: <i style="color: #267f99">T =&gt; string | null</i>): <i style="color: #267f99"><a href="/Decoder.html" style="color: inherit">Decoder</a>&lt;T&gt;</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L215-L233 'Source')
{: .signature}

Adds an extra predicate to a decoder. The new decoder is like the
Expand Down Expand Up @@ -215,7 +215,7 @@ decoder.verify({ id: 123, _name: 'Vincent' }) // throws: "Disallowed keys: _n

---

<a name="describe" href="#describe">#</a> **.describe**(message: <i style="color: #267f99">string</i>): <i style="color: #267f99"><a href="/Decoder.html" style="color: inherit">Decoder</a>&lt;T&gt;</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L199-L216 'Source')
<a name="describe" href="#describe">#</a> **.describe**(message: <i style="color: #267f99">string</i>): <i style="color: #267f99"><a href="/Decoder.html" style="color: inherit">Decoder</a>&lt;T&gt;</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L235-L252 'Source')
{: .signature}

Uses the given decoder, but will use an alternative error message in case it rejects. This can be used to simplify or shorten otherwise long or low-level/technical errors.
Expand All @@ -233,7 +233,7 @@ const vowel = decoder.describe('Must be vowel');

---

<a name="then" href="#then">#</a> **.then**&lt;<i style="color: #267f99">V</i>&gt;(next: <i style="color: #267f99">(blob: T, ok, err) =&gt; DecodeResult&lt;V&gt;</i>): <i style="color: #267f99"><a href="/Decoder.html" style="color: inherit">Decoder</a>&lt;V&gt;</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L153-L177 'Source')
<a name="then" href="#then">#</a> **.then**&lt;<i style="color: #267f99">V</i>&gt;(next: <i style="color: #267f99">(blob: T, ok, err) =&gt; DecodeResult&lt;V&gt;</i>): <i style="color: #267f99"><a href="/Decoder.html" style="color: inherit">Decoder</a>&lt;V&gt;</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L189-L213 'Source')
{: .signature}

Chain together the current decoder with another.
Expand All @@ -254,5 +254,5 @@ about its input and avoid re-refining inputs.
If it helps, you can think of `define(...)` as equivalent to
`unknown.then(...)`.

<!--[[[end]]] (checksum: e87df21a00a397934d055dd006a04c38) -->
<!--[[[end]]] (checksum: e541882267654ddfa8c2bdf606d5fb3c) -->
<!-- prettier-ignore-end -->
4 changes: 2 additions & 2 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1189,7 +1189,7 @@ try all decoders one by one.

---

<a name="define" href="#define">#</a> **define**&lt;<i style="color: #267f99">T</i>&gt;(fn: <i style="color: #267f99">(blob: unknown, ok, err) =&gt; DecodeResult&lt;T&gt;</i>): <i style="color: #267f99"><a href="/Decoder.html" style="color: inherit">Decoder</a>&lt;T&gt;</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L76-L250 'Source')
<a name="define" href="#define">#</a> **define**&lt;<i style="color: #267f99">T</i>&gt;(fn: <i style="color: #267f99">(blob: unknown, ok, err) =&gt; DecodeResult&lt;T&gt;</i>): <i style="color: #267f99"><a href="/Decoder.html" style="color: inherit">Decoder</a>&lt;T&gt;</i> [<small>(source)</small>](https://github.com/nvie/decoders/tree/main/src/Decoder.js#L112-L286 'Source')
{: .signature}

Defines a new `Decoder<T>`, by implementing a custom acceptance function.
Expand Down Expand Up @@ -1330,5 +1330,5 @@ const treeDecoder: Decoder<Tree> = object({
});
```

<!--[[[end]]] (checksum: 2a20c44d31ca8f979363179590354b65) -->
<!--[[[end]]] (checksum: aade6f5b0e8e5276ca24a90d54960030) -->
<!-- prettier-ignore-end -->
46 changes: 41 additions & 5 deletions src/Decoder.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,49 @@ export type AcceptanceFn<T, InputT = mixed> = (
) => DecodeResult<T>;

export type Decoder<T> = {|
verify(blob: mixed, formatterFn?: Formatter): T,
/**
* Verifies untrusted input. Either returns a value, or throws a decoding
* error.
*/
verify(blob: mixed, formatter?: Formatter): T,

/**
* Verifies untrusted input. Either returns a value, or returns undefined.
*/
value(blob: mixed): T | void,

/**
* Verifies untrusted input. Always returns a DecodeResult, which is either
* an "ok" value or an "error" annotation.
*/
decode(blob: mixed): DecodeResult<T>,

/**
* Build a new decoder from the the current one, with an extra acceptance
* criterium.
*/
refine(predicateFn: (value: T) => boolean, errmsg: string): Decoder<T>,

/**
* Build a new decoder from the current one, with an extra rejection
* criterium.
*/
reject(rejectFn: (value: T) => string | Annotation | null): Decoder<T>,

/**
* Build a new decoder from the current one, modifying its outputted value.
*/
transform<V>(transformFn: (value: T) => V): Decoder<V>,

/**
* Build a new decoder from the current one, with a mutated error message
* in case of a rejection.
*/
describe(message: string): Decoder<T>,

/**
* Chain together the current decoder with another acceptance function.
*/
then<V>(next: AcceptanceFn<V, T>): Decoder<V>,

// Experimental APIs (please don't rely on these yet)
Expand All @@ -36,13 +72,13 @@ export type Decoder<T> = {|
*
* You can use it on types:
*
* DecoderType<Decoder<string>> // => string
* DecoderType<Decoder<number[]>> // => number[]
* DecoderType<Decoder<string>> // string
* DecoderType<Decoder<number[]>> // number[]
*
* Or on "values", by using the `typeof` keyword:
*
* DecoderType<typeof array(string)> // => string[]
* DecoderType<typeof truthy> // => boolean
* DecoderType<typeof string> // string
* DecoderType<typeof truthy> // boolean
*
*/
export type DecoderType<D> = $Call<<T>(Decoder<T>) => T, D>;
Expand Down
63 changes: 63 additions & 0 deletions src/types/Decoder.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,83 @@ export type AcceptanceFn<T, InputT = unknown> = (
) => DecodeResult<T>;

export interface Decoder<T> {
/**
* Verifies untrusted input. Either returns a value, or throws a decoding
* error.
*/
verify(blob: unknown, formatterFn?: (ann: Annotation) => string | Error): T;

/**
* Verifies untrusted input. Either returns a value, or returns undefined.
*/
value(blob: unknown): T | undefined;

/**
* Verifies untrusted input. Always returns a DecodeResult, which is either
* an "ok" value or an "error" annotation.
*/
decode(blob: unknown): DecodeResult<T>;

/**
* Build a new decoder from the the current one, with an extra acceptance
* criterium.
*/
refine<N extends T>(predicate: (value: T) => value is N, msg: string): Decoder<N>;
refine(predicate: (value: T) => boolean, msg: string): Decoder<T>;

/**
* Build a new decoder from the current one, with an extra rejection
* criterium.
*/
reject(rejectFn: (value: T) => string | Annotation | null): Decoder<T>;

/**
* Build a new decoder from the current one, modifying its outputted value.
*/
transform<V>(transformFn: (value: T) => V): Decoder<V>;

/**
* Build a new decoder from the current one, with a mutated error message
* in case of a rejection.
*/
describe(message: string): Decoder<T>;

/**
* Chain together the current decoder with another acceptance function.
*/
then<V>(next: AcceptanceFn<V, T>): Decoder<V>;

// Experimental APIs (please don't rely on these yet)
peek_UNSTABLE<V>(next: AcceptanceFn<V, [unknown, T]>): Decoder<V>;
}

/**
* Helper type to return the "type" of a Decoder.
*
* You can use it on types:
*
* DecoderType<Decoder<string>> // string
* DecoderType<Decoder<number[]>> // number[]
*
* Or on "values", by using the `typeof` keyword:
*
* DecoderType<typeof string> // string
* DecoderType<typeof truthy> // boolean
*
*/
export type DecoderType<T> = T extends Decoder<infer V> ? V : never;

/**
* Defines a new `Decoder<T>`, by implementing a custom acceptance function.
* The function receives three arguments:
*
* 1. `blob` - the raw/unknown input (aka your external data)
* 2. `ok` - Call `ok(value)` to accept the input and return ``value``
* 3. `err` - Call `err(message)` to reject the input with error ``message``
*
* The expected return value should be a `DecodeResult<T>`, which can be
* obtained by returning the result of calling the provided `ok` or `err`
* helper functions. Please note that `ok()` and `err()` don't perform side
* effects! You'll need to _return_ those values.
*/
export function define<T>(fn: AcceptanceFn<T>): Decoder<T>;
22 changes: 22 additions & 0 deletions src/types/lib/arrays.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,33 @@

import { Decoder } from '../Decoder';

/**
* Accepts any array, but doesn't validate its items further.
*
* "poja" means "plain old JavaScript array", a play on `pojo()`.
*/
export const poja: Decoder<unknown[]>;

/**
* Accepts arrays of whatever the given decoder accepts.
*/
export function array<T>(decoder: Decoder<T>): Decoder<T[]>;

/**
* Like `array()`, but will reject arrays with 0 elements.
*/
export function nonEmptyArray<T>(decoder: Decoder<T>): Decoder<[T, ...T[]]>;

/**
* Similar to `array()`, but returns the result as an [ES6
* Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set).
*/
export function set<T>(decoder: Decoder<T>): Decoder<Set<T>>;

/**
* Accepts a tuple (an array with exactly _n_ items) of values accepted by the
* _n_ given decoders.
*/
export function tuple<A>(a: Decoder<A>): Decoder<[A]>;
export function tuple<A, B>(a: Decoder<A>, b: Decoder<B>): Decoder<[A, B]>;
export function tuple<A, B, C>(
Expand Down
57 changes: 56 additions & 1 deletion src/types/lib/basics.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
import { Decoder, Scalar } from '../Decoder';

/**
* Accepts and returns only the literal `null` value.
*/
export const null_: Decoder<null>;

/**
* Accepts and returns only the literal `undefined` value.
*/
export const undefined_: Decoder<undefined>;

/**
* Accepts whatever the given decoder accepts, or `undefined`.
*
* If a default value is explicitly provided, return that instead in the
* `undefined` case.
*/
export function optional<T>(decoder: Decoder<T>): Decoder<T | undefined>;
export function optional<T, V extends Scalar>(
decoder: Decoder<T>,
Expand All @@ -11,6 +25,13 @@ export function optional<T, V>(
decoder: Decoder<T>,
defaultValue: (() => V) | V,
): Decoder<NonNullable<T> | V>;

/**
* Accepts whatever the given decoder accepts, or `null`.
*
* If a default value is explicitly provided, return that instead in the `null`
* case.
*/
export function nullable<T>(decoder: Decoder<T>): Decoder<T | null>;
export function nullable<T, V extends Scalar>(
decoder: Decoder<T>,
Expand All @@ -20,6 +41,13 @@ export function nullable<T, V>(
decoder: Decoder<T>,
defaultValue: (() => V) | V,
): Decoder<NonNullable<T> | V>;

/**
* Accepts whatever the given decoder accepts, or `null`, or `undefined`.
*
* If a default value is explicitly provided, return that instead in the
* `null`/`undefined` case.
*/
export function maybe<T>(decoder: Decoder<T>): Decoder<T | null | undefined>;
export function maybe<T, V extends Scalar>(
decoder: Decoder<T>,
Expand All @@ -29,10 +57,37 @@ export function maybe<T, V>(
decoder: Decoder<T>,
defaultValue: (() => V) | V,
): Decoder<NonNullable<T> | V>;

/**
* Accepts only the given constant value.
*/
export function constant<T extends Scalar>(value: T): Decoder<T>;

/**
* Accepts anything, completely ignores it, and always returns the provided
* value instead.
*
* This is useful to manually add extra fields to object decoders.
*/
export function always<T extends Scalar>(value: T): Decoder<T>;
export function always<T>(value: (() => T) | T): Decoder<T>;

/**
* Alias of always.
*/
export function hardcoded<T extends Scalar>(value: T): Decoder<T>;
export function hardcoded<T>(value: (() => T) | T): Decoder<T>;
export const mixed: Decoder<unknown>;

/**
* Accepts anything and returns it unchanged.
*
* Useful for situation in which you don't know or expect a specific type. Of
* course, the downside is that you won't know the type of the value statically
* and you'll have to further refine it yourself.
*/
export const unknown: Decoder<unknown>;

/**
* Alias of unknown.
*/
export const mixed: Decoder<unknown>;
11 changes: 11 additions & 0 deletions src/types/lib/booleans.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import { Decoder } from '../Decoder';

/**
* Accepts and returns booleans.
*/
export const boolean: Decoder<boolean>;

/**
* Accepts anything and will return its "truth" value. Will never reject.
*/
export const truthy: Decoder<boolean>;

/**
* Accepts numbers, but return their boolean representation.
*/
export const numericBoolean: Decoder<boolean>;
Loading

0 comments on commit 4dfbbe2

Please sign in to comment.