decoders 2.0.0-beta12 → 2.0.0-beta13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Decoder.mjs CHANGED
@@ -12,38 +12,52 @@ function noThrow(fn) {
12
12
  }
13
13
  };
14
14
  }
15
+
16
+ function format(err, formatter) {
17
+ var formatted = formatter(err); // Formatter functions may return a string or an error for convenience of
18
+ // writing them. If it already returns an Error, return it unmodified. If
19
+ // it returns a string, wrap it in a "Decoding error" instance.
20
+
21
+ if (typeof formatted === 'string') {
22
+ var _err = new Error('\n' + formatted);
23
+
24
+ _err.name = 'Decoding error';
25
+ return _err;
26
+ } else {
27
+ return formatted;
28
+ }
29
+ }
15
30
  /**
16
31
  * Defines a new `Decoder<T>`, by implementing a custom acceptance function.
17
32
  * The function receives three arguments:
18
33
  *
19
34
  * 1. `blob` - the raw/unknown input (aka your external data)
20
35
  * 2. `ok` - Call `ok(value)` to accept the input and return ``value``
21
- * 3. `err` - Call `err(message)` to reject the input and use "message" in the
22
- * annotation
36
+ * 3. `err` - Call `err(message)` to reject the input with error ``message``
23
37
  *
24
38
  * The expected return value should be a `DecodeResult<T>`, which can be
25
- * obtained by returning the result from the provided `ok` or `err` helper
26
- * functions.
39
+ * obtained by returning the result of calling the provided `ok` or `err`
40
+ * helper functions. Please note that `ok()` and `err()` don't perform side
41
+ * effects! You'll need to _return_ those values.
27
42
  */
28
43
 
29
44
 
30
- export function define(decodeFn) {
45
+ export function define(fn) {
31
46
  /**
32
- * Validates the raw/untrusted/unknown input and either accepts or rejects
33
- * it.
47
+ * Verifies the untrusted/unknown input and either accepts or rejects it.
34
48
  *
35
49
  * Contrasted with `.verify()`, calls to `.decode()` will never fail and
36
50
  * instead return a result type.
37
51
  */
38
52
  function decode(blob) {
39
- return decodeFn(blob, makeOk, function (msg) {
53
+ return fn(blob, makeOk, function (msg) {
40
54
  return makeErr(typeof msg === 'string' ? annotate(blob, msg) : msg);
41
55
  });
42
56
  }
43
57
  /**
44
- * Verified the (raw/untrusted/unknown) input and either accepts or rejects
45
- * it. When accepted, returns the decoded `T` value directly. Otherwise
46
- * fail with a runtime error.
58
+ * Verifies the untrusted/unknown input and either accepts or rejects it.
59
+ * When accepted, returns a value of type `T`. Otherwise fail with
60
+ * a runtime error.
47
61
  */
48
62
 
49
63
 
@@ -57,26 +71,13 @@ export function define(decodeFn) {
57
71
  if (result.ok) {
58
72
  return result.value;
59
73
  } else {
60
- // Formatters may return a string or an error for convenience of
61
- // writing them. If it already returns an Error, throw it
62
- // unmodified. If it returns a string, wrap it in a "Decoding
63
- // error" instance from it and throw that.
64
- var strOrErr = formatter(result.error);
65
-
66
- if (typeof strOrErr === 'string') {
67
- var _err = new Error('\n' + strOrErr);
68
-
69
- _err.name = 'Decoding error';
70
- throw _err;
71
- } else {
72
- throw strOrErr;
73
- }
74
+ throw format(result.error, formatter);
74
75
  }
75
76
  }
76
77
  /**
77
- * Verified the (raw/untrusted/unknown) input and either accepts or rejects
78
- * it. When accepted, returns the decoded `T` value directly. Otherwise
79
- * returns `undefined`.
78
+ * Verifies the untrusted/unknown input and either accepts or rejects it.
79
+ * When accepted, returns the decoded `T` value directly. Otherwise returns
80
+ * `undefined`.
80
81
  *
81
82
  * Use this when you're not interested in programmatically handling the
82
83
  * error message.
@@ -114,23 +115,21 @@ export function define(decodeFn) {
114
115
  /**
115
116
  * Chain together the current decoder with another.
116
117
  *
117
- * First, the current decoder must accept the input. If so, it will pass
118
- * the successfully decoded result to the given ``next`` function to
119
- * further decide whether or not the value should get accepted or rejected.
118
+ * > _**NOTE:** This is an advanced, low-level, API. It's not recommended
119
+ * > to reach for this construct unless there is no other way. Most cases can
120
+ * > be covered more elegantly by `.transform()` or `.refine()` instead._
120
121
  *
121
- * The argument to `.then()` is a decoding function, just like one you
122
- * would pass to `define()`. The key difference with `define()` is that
123
- * `define()` must always assume an ``unknown`` input, whereas with
124
- * a `.then()` call the provided ``next`` function will receive a ``T`` as
125
- * its input. This will allow the function to make a stronger assumption
126
- * about its input.
122
+ * If the current decoder accepts an input, the resulting ``T`` value will
123
+ * get passed into the given ``next`` acceptance function to further decide
124
+ * whether or not the value should get accepted or rejected.
127
125
  *
128
- * If it helps, you can think of `define(nextFn)` as equivalent to
129
- * `unknown.then(nextFn)`.
126
+ * This works similar to how you would `define()` a new decoder, except
127
+ * that the ``blob`` param will now be ``T`` (a known type), rather than
128
+ * ``unknown``. This will allow the function to make a stronger assumption
129
+ * about its input and avoid re-refining inputs.
130
130
  *
131
- * This is an advanced, low-level, decoder. It's not recommended to reach
132
- * for this low-level construct when implementing custom decoders. Most
133
- * cases can be covered by `.transform()` or `.refine()`.
131
+ * If it helps, you can think of `define(...)` as equivalent to
132
+ * `unknown.then(...)`.
134
133
  */
135
134
 
136
135
 
package/README.md CHANGED
@@ -22,14 +22,19 @@ solve both of these problems at once.**
22
22
  ```typescript
23
23
  import { array, iso8601, number, object, optional, string } from 'decoders';
24
24
 
25
- // External data, for example JSON.parse()'ed from a request payload
25
+ //
26
+ // Incoming data at runtime
27
+ //
26
28
  const externalData = {
27
29
  id: 123,
28
30
  name: 'Alison Roberts',
29
- createdAt: '2022-01-11T12:26:37.024Z',
30
- tags: ['foo', 'bar', 'qux'],
31
+ createdAt: '1994-01-11T12:26:37.024Z',
32
+ tags: ['foo', 'bar'],
31
33
  };
32
34
 
35
+ //
36
+ // Write the decoder (= what you expect the data to look like)
37
+ //
33
38
  const userDecoder = object({
34
39
  id: number,
35
40
  name: string,
@@ -37,82 +42,96 @@ const userDecoder = object({
37
42
  tags: array(string),
38
43
  });
39
44
 
40
- // NOTE: TypeScript will automatically infer this type for the `user` variable
41
- // interface User {
42
- // id: number;
43
- // name: string;
44
- // createdAt?: Date;
45
- // tags: string[];
46
- // }
47
-
45
+ //
46
+ // Call .verify() on the incoming data
47
+ //
48
48
  const user = userDecoder.verify(externalData);
49
+ // ^^^^
50
+ // TypeScript can automatically infer this type now:
51
+ //
52
+ // {
53
+ // id: number;
54
+ // name: string;
55
+ // createdAt?: Date;
56
+ // tags: string[];
57
+ // }
58
+ //
49
59
  ```
50
60
 
51
61
  ## Documentation
52
62
 
53
- <div id="guard"></div>
54
- <div id="primitives"></div>
55
- <div id="compositions"></div>
63
+ <div id="$DecoderType"></div>
64
+ <div id="DecodeResult"></div>
65
+ <div id="Decoder"></div>
66
+ <div id="DecoderType"></div>
67
+ <div id="Guard"></div>
68
+ <div id="JSONArray"></div>
69
+ <div id="JSONObject"></div>
70
+ <div id="JSONValue"></div>
71
+ <div id="Scalar"></div>
72
+ <div id="adding-predicates"></div>
73
+ <div id="always"></div>
74
+ <div id="anyNumber"></div>
75
+ <div id="array"></div>
76
+ <div id="boolean"></div>
56
77
  <div id="building-custom-decoders"></div>
57
- <div id="number"></div>
58
- <div id="integer"></div>
59
- <div id="positiveNumber"></div>
60
- <div id="positiveInteger"></div>
61
- <div id="string"></div>
62
- <div id="nonEmptyString"></div>
63
- <div id="regex"></div>
78
+ <div id="compose"></div>
79
+ <div id="compositions"></div>
80
+ <div id="constant"></div>
81
+ <div id="date"></div>
82
+ <div id="define"></div>
83
+ <div id="describe"></div>
84
+ <div id="dict"></div>
85
+ <div id="either"></div>
64
86
  <div id="email"></div>
65
- <div id="url"></div>
87
+ <div id="exact"></div>
88
+ <div id="fail"></div>
89
+ <div id="guard"></div>
90
+ <div id="hardcoded"></div>
66
91
  <div id="httpsUrl"></div>
67
- <div id="uuid"></div>
68
- <div id="uuidv1"></div>
69
- <div id="uuidv4"></div>
70
- <div id="boolean"></div>
71
- <div id="string"></div>
72
- <div id="truthy"></div>
73
- <div id="numericBoolean"></div>
74
- <div id="date"></div>
92
+ <div id="inexact"></div>
93
+ <div id="instanceOf"></div>
94
+ <div id="integer"></div>
75
95
  <div id="iso8601"></div>
76
- <div id="null_"></div>
77
- <div id="undefined_"></div>
78
- <div id="constant"></div>
79
- <div id="always"></div>
80
- <div id="hardcoded"></div>
81
- <div id="never"></div>
82
- <div id="fail"></div>
83
- <div id="unknown"></div>
84
- <div id="mixed"></div>
85
- <div id="optional"></div>
86
- <div id="nullable"></div>
96
+ <div id="json"></div>
97
+ <div id="jsonArray"></div>
98
+ <div id="jsonObject"></div>
99
+ <div id="lazy"></div>
100
+ <div id="mapping"></div>
87
101
  <div id="maybe"></div>
88
- <div id="array"></div>
102
+ <div id="mixed"></div>
103
+ <div id="never"></div>
89
104
  <div id="nonEmptyArray"></div>
90
- <div id="poja"></div>
91
- <div id="tuple"></div>
92
- <div id="set"></div>
105
+ <div id="nonEmptyString"></div>
106
+ <div id="null_"></div>
107
+ <div id="nullable"></div>
108
+ <div id="number"></div>
109
+ <div id="numericBoolean"></div>
93
110
  <div id="object"></div>
94
- <div id="exact"></div>
95
- <div id="inexact"></div>
96
- <div id="pojo"></div>
97
- <div id="dict"></div>
98
- <div id="mapping"></div>
99
- <div id="json"></div>
100
- <div id="jsonObject"></div>
101
- <div id="jsonArray"></div>
102
- <div id="either"></div>
103
- <div id="taggedUnion"></div>
104
111
  <div id="oneOf"></div>
105
- <div id="instanceOf"></div>
106
- <div id="transform"></div>
107
- <div id="compose"></div>
112
+ <div id="optional"></div>
113
+ <div id="poja"></div>
114
+ <div id="pojo"></div>
115
+ <div id="positiveInteger"></div>
116
+ <div id="positiveNumber"></div>
108
117
  <div id="predicate"></div>
109
118
  <div id="prep"></div>
110
- <div id="describe"></div>
111
- <div id="lazy"></div>
119
+ <div id="primitives"></div>
120
+ <div id="regex"></div>
121
+ <div id="set"></div>
122
+ <div id="string"></div>
123
+ <div id="taggedUnion"></div>
112
124
  <div id="the-difference-between-object-exact-and-inexact"></div>
113
- <div id="building-custom-decoders"></div>
125
+ <div id="transform"></div>
114
126
  <div id="transformation"></div>
115
- <div id="adding-predicates"></div>
127
+ <div id="truthy"></div>
128
+ <div id="tuple"></div>
129
+ <div id="undefined_"></div>
130
+ <div id="unknown"></div>
131
+ <div id="url"></div>
132
+ <div id="uuid"></div>
133
+ <div id="uuidv1"></div>
134
+ <div id="uuidv4"></div>
116
135
 
117
136
  Documentation for v1 can be found
118
137
  [here](https://github.com/nvie/decoders/tree/v1.25.5#readme).
package/format.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { Annotation } from './annotate';
2
2
 
3
- export function formatInline(ann: Annotation): string;
4
- export function formatShort(ann: Annotation): string;
3
+ export type Formatter = (err: Annotation) => string | Error;
4
+
5
+ export const formatInline: Formatter;
6
+ export const formatShort: Formatter;
package/format.js.flow CHANGED
@@ -3,6 +3,8 @@
3
3
  import { summarize as _summarize, asDate, INDENT, indent, isMultiline } from './_utils';
4
4
  import type { Annotation, ArrayAnnotation, ObjectAnnotation } from './annotate';
5
5
 
6
+ export type Formatter = (err: Annotation) => string | Error;
7
+
6
8
  function serializeString(s: string, width: number = 80): string {
7
9
  // Full string
8
10
  // Abbreviated to $maxlen i.e. "Vincent Driess..." [truncated]
package/index.d.ts CHANGED
@@ -17,7 +17,7 @@ export { array, nonEmptyArray, poja, set, tuple } from './lib/arrays';
17
17
  export { boolean, numericBoolean, truthy } from './lib/booleans';
18
18
  export { date, iso8601 } from './lib/dates';
19
19
  export { dict, exact, inexact, mapping, object, pojo } from './lib/objects';
20
- export { either, oneOf, taggedUnion, dispatch } from './lib/unions';
20
+ export { either, oneOf, taggedUnion } from './lib/unions';
21
21
  export {
22
22
  email,
23
23
  httpsUrl,
package/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  exports.__esModule = true;
4
- exports.uuidv4 = exports.uuidv1 = exports.uuid = exports.url = exports.unknown = exports.undefined_ = exports.tuple = exports.truthy = exports.taggedUnion = exports.string = exports.set = exports.regex = exports.prep = exports.positiveNumber = exports.positiveInteger = exports.pojo = exports.poja = exports.optional = exports.oneOf = exports.object = exports.numericBoolean = exports.number = exports.nullable = exports.null_ = exports.nonEmptyString = exports.nonEmptyArray = exports.never = exports.mixed = exports.maybe = exports.mapping = exports.lazy = exports.jsonObject = exports.jsonArray = exports.json = exports.iso8601 = exports.integer = exports.instanceOf = exports.inexact = exports.httpsUrl = exports.hardcoded = exports.fail = exports.exact = exports.email = exports.either = exports.dispatch = exports.dict = exports.define = exports.date = exports.constant = exports["boolean"] = exports.array = exports.anyNumber = exports.always = void 0;
4
+ exports.uuidv4 = exports.uuidv1 = exports.uuid = exports.url = exports.unknown = exports.undefined_ = exports.tuple = exports.truthy = exports.taggedUnion = exports.string = exports.set = exports.regex = exports.prep = exports.positiveNumber = exports.positiveInteger = exports.pojo = exports.poja = exports.optional = exports.oneOf = exports.object = exports.numericBoolean = exports.number = exports.nullable = exports.null_ = exports.nonEmptyString = exports.nonEmptyArray = exports.never = exports.mixed = exports.maybe = exports.mapping = exports.lazy = exports.jsonObject = exports.jsonArray = exports.json = exports.iso8601 = exports.integer = exports.instanceOf = exports.inexact = exports.httpsUrl = exports.hardcoded = exports.fail = exports.exact = exports.email = exports.either = exports.dict = exports.define = exports.date = exports.constant = exports["boolean"] = exports.array = exports.anyNumber = exports.always = void 0;
5
5
 
6
6
  var _Decoder = require("./Decoder");
7
7
 
@@ -53,7 +53,6 @@ var _unions = require("./lib/unions");
53
53
  exports.either = _unions.either;
54
54
  exports.oneOf = _unions.oneOf;
55
55
  exports.taggedUnion = _unions.taggedUnion;
56
- exports.dispatch = _unions.dispatch;
57
56
 
58
57
  var _strings = require("./lib/strings");
59
58
 
package/index.js.flow CHANGED
@@ -21,7 +21,7 @@ export { array, nonEmptyArray, poja, set, tuple } from './lib/arrays';
21
21
  export { boolean, numericBoolean, truthy } from './lib/booleans';
22
22
  export { date, iso8601 } from './lib/dates';
23
23
  export { dict, exact, inexact, mapping, object, pojo } from './lib/objects';
24
- export { either, oneOf, taggedUnion, dispatch } from './lib/unions';
24
+ export { either, oneOf, taggedUnion } from './lib/unions';
25
25
  export {
26
26
  email,
27
27
  httpsUrl,
package/index.mjs CHANGED
@@ -4,7 +4,7 @@ export { array, nonEmptyArray, poja, set, tuple } from './lib/arrays.mjs';
4
4
  export { boolean, numericBoolean, truthy } from './lib/booleans.mjs';
5
5
  export { date, iso8601 } from './lib/dates.mjs';
6
6
  export { dict, exact, inexact, mapping, object, pojo } from './lib/objects.mjs';
7
- export { either, oneOf, taggedUnion, dispatch } from './lib/unions.mjs';
7
+ export { either, oneOf, taggedUnion } from './lib/unions.mjs';
8
8
  export { email, httpsUrl, nonEmptyString, regex, string, url, uuid, uuidv1, uuidv4 } from './lib/strings.mjs';
9
9
  export { fail, instanceOf, lazy, never, prep } from './lib/utilities.mjs';
10
10
  export { anyNumber, integer, number, positiveInteger, positiveNumber } from './lib/numbers.mjs';
package/lib/arrays.d.ts CHANGED
@@ -2,11 +2,33 @@
2
2
 
3
3
  import { Decoder } from '../Decoder';
4
4
 
5
+ /**
6
+ * Accepts any array, but doesn't validate its items further.
7
+ *
8
+ * "poja" means "plain old JavaScript array", a play on `pojo()`.
9
+ */
5
10
  export const poja: Decoder<unknown[]>;
11
+
12
+ /**
13
+ * Accepts arrays of whatever the given decoder accepts.
14
+ */
6
15
  export function array<T>(decoder: Decoder<T>): Decoder<T[]>;
16
+
17
+ /**
18
+ * Like `array()`, but will reject arrays with 0 elements.
19
+ */
7
20
  export function nonEmptyArray<T>(decoder: Decoder<T>): Decoder<[T, ...T[]]>;
21
+
22
+ /**
23
+ * Similar to `array()`, but returns the result as an [ES6
24
+ * Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set).
25
+ */
8
26
  export function set<T>(decoder: Decoder<T>): Decoder<Set<T>>;
9
27
 
28
+ /**
29
+ * Accepts a tuple (an array with exactly _n_ items) of values accepted by the
30
+ * _n_ given decoders.
31
+ */
10
32
  export function tuple<A>(a: Decoder<A>): Decoder<[A]>;
11
33
  export function tuple<A, B>(a: Decoder<A>, b: Decoder<B>): Decoder<[A, B]>;
12
34
  export function tuple<A, B, C>(
package/lib/arrays.js CHANGED
@@ -101,10 +101,6 @@ var ntuple = function ntuple(n) {
101
101
  }; // prettier-ignore
102
102
 
103
103
 
104
- /**
105
- * Accepts a tuple (an array with exactly _n_ items) of values accepted by the
106
- * _n_ given decoders.
107
- */
108
104
  function _tuple() {
109
105
  for (var _len = arguments.length, decoders = new Array(_len), _key = 0; _key < _len; _key++) {
110
106
  decoders[_key] = arguments[_key];
@@ -133,6 +129,11 @@ function _tuple() {
133
129
  }
134
130
  });
135
131
  }
132
+ /**
133
+ * Accepts a tuple (an array with exactly _n_ items) of values accepted by the
134
+ * _n_ given decoders.
135
+ */
136
+
136
137
 
137
138
  var tuple = _tuple;
138
139
  exports.tuple = tuple;
@@ -97,7 +97,7 @@ const ntuple = (n: number) =>
97
97
  poja.refine((arr) => arr.length === n, `Must be a ${n}-tuple`);
98
98
 
99
99
  // prettier-ignore
100
- interface TupleFuncSignature {
100
+ interface TupleT {
101
101
  <A>(a: Decoder<A>): Decoder<[A]>;
102
102
  <A, B>(a: Decoder<A>, b: Decoder<B>): Decoder<[A, B]>;
103
103
  <A, B, C>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>): Decoder<[A, B, C]>;
@@ -106,10 +106,6 @@ interface TupleFuncSignature {
106
106
  <A, B, C, D, E, F>(a: Decoder<A>, b: Decoder<B>, c: Decoder<C>, d: Decoder<D>, e: Decoder<E>, f: Decoder<F>): Decoder<[A, B, C, D, E, F]>;
107
107
  }
108
108
 
109
- /**
110
- * Accepts a tuple (an array with exactly _n_ items) of values accepted by the
111
- * _n_ given decoders.
112
- */
113
109
  function _tuple(...decoders: $ReadOnlyArray<Decoder<mixed>>): Decoder<Array<mixed>> {
114
110
  return ntuple(decoders.length).then((blobs, ok, err) => {
115
111
  let allOk = true;
@@ -135,4 +131,8 @@ function _tuple(...decoders: $ReadOnlyArray<Decoder<mixed>>): Decoder<Array<mixe
135
131
  });
136
132
  }
137
133
 
138
- export const tuple: TupleFuncSignature = (_tuple: _Any);
134
+ /**
135
+ * Accepts a tuple (an array with exactly _n_ items) of values accepted by the
136
+ * _n_ given decoders.
137
+ */
138
+ export const tuple: TupleT = (_tuple: _Any);
package/lib/arrays.mjs CHANGED
@@ -87,10 +87,6 @@ var ntuple = function ntuple(n) {
87
87
  }; // prettier-ignore
88
88
 
89
89
 
90
- /**
91
- * Accepts a tuple (an array with exactly _n_ items) of values accepted by the
92
- * _n_ given decoders.
93
- */
94
90
  function _tuple() {
95
91
  for (var _len = arguments.length, decoders = new Array(_len), _key = 0; _key < _len; _key++) {
96
92
  decoders[_key] = arguments[_key];
@@ -119,5 +115,10 @@ function _tuple() {
119
115
  }
120
116
  });
121
117
  }
118
+ /**
119
+ * Accepts a tuple (an array with exactly _n_ items) of values accepted by the
120
+ * _n_ given decoders.
121
+ */
122
+
122
123
 
123
124
  export var tuple = _tuple;
package/lib/basics.d.ts CHANGED
@@ -1,38 +1,93 @@
1
1
  import { Decoder, Scalar } from '../Decoder';
2
2
 
3
+ /**
4
+ * Accepts and returns only the literal `null` value.
5
+ */
3
6
  export const null_: Decoder<null>;
7
+
8
+ /**
9
+ * Accepts and returns only the literal `undefined` value.
10
+ */
4
11
  export const undefined_: Decoder<undefined>;
12
+
13
+ /**
14
+ * Accepts whatever the given decoder accepts, or `undefined`.
15
+ *
16
+ * If a default value is explicitly provided, return that instead in the
17
+ * `undefined` case.
18
+ */
5
19
  export function optional<T>(decoder: Decoder<T>): Decoder<T | undefined>;
6
20
  export function optional<T, V extends Scalar>(
7
21
  decoder: Decoder<T>,
8
- defaultValue: V,
22
+ defaultValue: (() => V) | V,
9
23
  ): Decoder<NonNullable<T> | V>;
10
24
  export function optional<T, V>(
11
25
  decoder: Decoder<T>,
12
- defaultValue: V,
26
+ defaultValue: (() => V) | V,
13
27
  ): Decoder<NonNullable<T> | V>;
28
+
29
+ /**
30
+ * Accepts whatever the given decoder accepts, or `null`.
31
+ *
32
+ * If a default value is explicitly provided, return that instead in the `null`
33
+ * case.
34
+ */
14
35
  export function nullable<T>(decoder: Decoder<T>): Decoder<T | null>;
15
36
  export function nullable<T, V extends Scalar>(
16
37
  decoder: Decoder<T>,
17
- defaultValue: V,
38
+ defaultValue: (() => V) | V,
18
39
  ): Decoder<NonNullable<T> | V>;
19
40
  export function nullable<T, V>(
20
41
  decoder: Decoder<T>,
21
- defaultValue: V,
42
+ defaultValue: (() => V) | V,
22
43
  ): Decoder<NonNullable<T> | V>;
44
+
45
+ /**
46
+ * Accepts whatever the given decoder accepts, or `null`, or `undefined`.
47
+ *
48
+ * If a default value is explicitly provided, return that instead in the
49
+ * `null`/`undefined` case.
50
+ */
23
51
  export function maybe<T>(decoder: Decoder<T>): Decoder<T | null | undefined>;
24
52
  export function maybe<T, V extends Scalar>(
25
53
  decoder: Decoder<T>,
26
- defaultValue: V,
54
+ defaultValue: (() => V) | V,
27
55
  ): Decoder<NonNullable<T> | V>;
28
56
  export function maybe<T, V>(
29
57
  decoder: Decoder<T>,
30
- defaultValue: V,
58
+ defaultValue: (() => V) | V,
31
59
  ): Decoder<NonNullable<T> | V>;
60
+
61
+ /**
62
+ * Accepts only the given constant value.
63
+ */
32
64
  export function constant<T extends Scalar>(value: T): Decoder<T>;
65
+
66
+ /**
67
+ * Accepts anything, completely ignores it, and always returns the provided
68
+ * value instead.
69
+ *
70
+ * This is useful to manually add extra fields to object decoders.
71
+ */
33
72
  export function always<T extends Scalar>(value: T): Decoder<T>;
34
- export function always<T>(value: T): Decoder<T>;
73
+ export function always<T>(value: (() => T) | T): Decoder<T>;
74
+
75
+ /**
76
+ * Alias of always.
77
+ */
35
78
  export function hardcoded<T extends Scalar>(value: T): Decoder<T>;
36
- export function hardcoded<T>(value: T): Decoder<T>;
37
- export const mixed: Decoder<unknown>;
79
+ export function hardcoded<T>(value: (() => T) | T): Decoder<T>;
80
+
81
+ /**
82
+ * Accepts anything and returns it unchanged.
83
+ *
84
+ * Useful for situation in which you don't know or expect a specific type. Of
85
+ * course, the downside is that you won't know the type of the value statically
86
+ * and you'll have to further refine it yourself.
87
+ */
38
88
  export const unknown: Decoder<unknown>;
89
+
90
+ /**
91
+ * Alias of unknown.
92
+ */
93
+ export const mixed: Decoder<unknown>;
package/lib/basics.js CHANGED
@@ -29,22 +29,26 @@ var undefined_or_null = (0, _Decoder.define)(function (blob, ok, err) {
29
29
  err('Must be undefined or null');
30
30
  });
31
31
 
32
- /**
33
- * Accepts whatever the given decoder accepts, or `null`, or `undefined`.
34
- */
35
32
  function _maybeish(emptyCase) {
36
33
  function _inner(decoder
37
34
  /* defaultValue */
38
35
  ) {
39
- var _arguments = arguments;
40
36
  var rv = (0, _unions.either)(emptyCase, decoder);
41
- return (// If a default value is provided...
42
- arguments.length >= 2 ? // ...then return the default value
43
- rv.transform(function (value) {
44
- return value != null ? value : _arguments[1];
45
- }) : // Otherwise the "normal" empty case
46
- rv
47
- );
37
+
38
+ if ( // If a default value is provided...
39
+ arguments.length >= 2) {
40
+ // ...then return the default value
41
+ var _defaultValue = arguments[1];
42
+
43
+ var _defaultValue2 = typeof _defaultValue === 'function' ? _defaultValue() : _defaultValue;
44
+
45
+ return rv.transform(function (value) {
46
+ return value != null ? value : _defaultValue2;
47
+ });
48
+ } else {
49
+ // Otherwise the "normal" empty case
50
+ return rv;
51
+ }
48
52
  }
49
53
 
50
54
  return _inner;
@@ -101,7 +105,13 @@ function constant(value) {
101
105
 
102
106
 
103
107
  function always(value) {
104
- return (0, _Decoder.define)(function (blob
108
+ return (0, _Decoder.define)(typeof value === 'function' ? function (blob
109
+ /* ignored */
110
+ , ok, _) {
111
+ return (// $FlowFixMe[incompatible-use]
112
+ ok(value())
113
+ );
114
+ } : function (blob
105
115
  /* ignored */
106
116
  , ok, _) {
107
117
  return ok(value);