elmish-decoder
v2.0.2
Published
A Elm-inspired implementation of Decoders for Typescript.
Downloads
2
Maintainers
Readme
elmish-decoder
Turn (untyped) any
values (for example coming from a configuration
file or some external API) into type-checked and type-safe Typescript values, without having to remember to update your schema/validation code manually! Instead, elmish-decoders
tries to use Typescript's type system as best as it can to catch most mistakes at compile-time, given sufficient type annotations.
This library is heavily inspired by Elm's Json.Decode, but adds some Typescript-specific features to it, making it easier to use while also catching more mistakes at compile time (compared to other Typescript libraries of course).
If you are comfortable with Elm/ML-like syntax, I highly recommend checking out their Intro to JSON decoders for an introduction behind the concepts of this library.
Getting started
Setup
elmish-decoder
was tested using the following versions:
- Node ^8.10
- Typescript ^3.1.0
This library works best if you use Typescript's strict
mode, enabling it to catch unhandled optional fields, unsafe Decoder casts, and incompatible constructor signatures.
To install the package, simply use npm
. Types are already included!
$ npm install --save elmish-decoder
A simple package.json
Decoder
Also try this out for yourself, and see what happens if you change the types!
import Decoder from 'elmish-decoder';
import fs = require('fs');
import semver = require('semver');
// A simplified version of the full package.json - Type
interface Dependencies {
[packageName: string]: semver.Range
}
interface PackageJson {
private?: boolean;
name: string;
version: semver.SemVer,
dependencies: Dependencies
devDependencies: Dependencies
}
// Parse a string, then pass it to the parseRange function
const rangeDecoder = Decoder.class(semver.Range, Decoder.string, Decoder.succeedOpt(true));
// Parse a string, then pass it to the parseVersion function.
// semver.parse returns `null` if the version is not valid, so
// we use `Decoder.filter` to validate the version afterwards.
const semverDecoder =
Decoder.function(semver.parse, Decoder.string, Decoder.succeedOpt(true))
.filterOptional("Version is not valid!");
// Dependencies always contain a Range as their value.
// There are no required fields, so the second parameter be empty.
const dependenciesDecoder = Decoder.dict<Dependencies>(rangeDecoder, {});
// Here we combine everything together to a PackageJson - Interface Decoder.
const packageJsonDecoder = Decoder.obj<PackageJson>({
private: Decoder.optional(Decoder.boolean),
name: Decoder.string,
version: semverDecoder,
dependencies: dependenciesDecoder,
devDependencies: dependenciesDecoder
});
const packageJsonStr = fs.readFileSync('./package.json', 'utf-8');
// Try it out on the local package.json!
// `runString` calls the first (left) function on a failure, and the second (right) function on success.
packageJsonDecoder.runString(console.error, console.log, packageJsonStr);
API Documentation
Index
This is an alphabetical list of all methods. If you know what you need and just want to see the documentation for it, you can use those links. Otherwise, I recommend reading the documentation in order.
Properties:
Static Functions:
- array, class, dict, fail, field, fieldOpt, function, guard, index, lazy, lazyOpt, obj, oneOf, optional, succeed, succeedOpt, tag, tuple
Instance Methods:
- andThen, decodeString, decodeValue, filter, filterGuard, filterOptional, fold, map, orDefaultValue, orElse, runString, runValue,
<Class>
Decoder<T
>
This class itself represents a Decoder knowing how to turn an any
into a T
.
Running Decoders
decodeString
▸ decodeString(jsonString: string
): T
Parses the given string into a JSON value and run the decoder on it. This function will fail if the input string is not valid JSON, or if the Decoder fails for some reason. throws: {DecoderError} An exception containing a list of all the things that went wrong.
Parameters:
| Param | Type |
| ------ | ------ |
| jsonString | string
|
Returns: T
The decoded object.
decodeValue
▸ decodeValue(input: any
): T
Decodes a given value, returning a type-checked, fully constructed version of it. This function will fail with an exception if the Decoder fails for some reason. throws: {DecoderError} An exception containing a list of all the things that went wrong.
Parameters:
| Param | Type |
| ------ | ------ |
| input | any
|
Returns: T
The decoded object.
runString
▸ runString<U
>(Left: function
, Right: function
, jsonString: string
): U
Parse a given string into a JSON value and run the decoder on it. Instead of throwing an exception, this method uses the functional approach of returning a 'Either' instance to distinquish success from failure.
This function catches any JSON errors and packages them into a Left
. Any other exceptions will not be caught by using this method.
Parameters:
| Param | Type | Description |
| ------ | ------ | ------ |
| Left | function
| 'Either' object constructor for the Failure case |
| Right | function
| 'Either' object constructor for the Success case |
| jsonString | string
| The string to parse and decode |
Returns: U
runValue
▸ runValue<U
>(Left: function
, Right: function
, input: any
): U
Decode a value, putting the result into an Either type. Instead of throwing an exception, this method uses the functional approach of returning a 'Either' instance to distinquish success from failure.
This function will not catch exceptions for you, but allows you to handle Decoder errors in a more functional way.
Internally, this library uses a custom 'Result' Applicative type.
Parameters:
| Param | Type | Description |
| ------ | ------ | ------ |
| Left | function
| 'Either' object constructor for the Failure case |
| Right | function
| 'Either' object constructor for the Success case |
| input | any
|
Returns: U
Primitives
<Static>
any
● any: Decoder<any
>
Do not decode the value, but return it as an untyped any
. This can be useful if you have a complex data structure you want to deal with later, or you just need to extract this value to pass it on, but you don't care about its structure.
<Static>
boolean
● boolean: Decoder<boolean
>
Decode a boolean. Only real boolean
objects will be accepted, so a string property "true"
or a numeric value 1
will result in a failure.
Decoder.boolean.decodeValue(true) // true
Decoder.boolean.decodeValue(1) // DecoderError
Decoder.boolean.decodeValue("true") // DecoderError
<Static>
float
● float: Decoder<number
>
Similar to Decoder.number
, this decoder accepts all floating point numbers, but does not allow for special values like NaN
or Infinite
, even if the object itself would be a valid number object.
Decoder.float.decodeValue(42) // ==> 42
Decoder.float.decodeValue("42") // ==> DecoderError
Decoder.float.decodeValue("NaN") // ==> DecoderError
Decoder.float.decodeValue(Infinity) // ==> DecoderError
Decoder.float.decodeValue("-Infinity") // ==> DecoderError
<Static>
int
● int: Decoder<number
>
Decode a number that is an integer. In contrast to the Decoder.number
decoder, this does not accept NaN
or Infinite
values, and also rejects all numbers that cannot be encoded as a 32-bit integer value, including all floating-point numbers.
Decoder.int.decodeValue(42) // ==> 42
Decoder.int.decodeValue(-0) // ==> -0
Decoder.int.decodeValue(0.5) // ==> DecoderError
Decoder.int.decodeValue(Math.pow(2, 32)) // ==> DecoderError
<Static>
number
● number: Decoder<number
>
Decode a number. Only real number
objects will be accepted. Strings are generally not seen as numbers, even if they only contain digits. The only exceptions to this rule are special string for "NaN"
and "Infinity"
, respectively.
Decoder.number.decodeValue(42) // ==> 42
Decoder.number.decodeValue("42") // ==> DecoderError
Decoder.number.decodeValue("NaN") // ==> NaN
Decoder.number.decodeValue(Infinity) // ==> Infinity
Decoder.number.decodeValue("-Infinity") // ==> -Infinity
<Static>
string
● string: Decoder<string
>
Decode string values. Only actual strings will be accepted by this decoder, so values like null
or any other value that could be .toString()
- ed are going to be rejected.
If you want to accept optional strings, consider using the optional modifier, or create Decoder chains using Decoder.oneOf.
Decoder.string.decodeValue("") // ==> ""
Decoder.string.decodeValue("Hello, Sailor!") // ==> "Hello, Sailor!"
Decoder.string.decodeValue(null) // ==> DecoderError
Decoder.string.decodeValue({ toString() { return ""; } }) // ==> DecoderError
Composite Types
<Static>
obj
▸ obj<T
>(configuration: DecoderTable
<T
>): Decoder<T
>
Create a typesafe decoder for an interface or a type alias. The configuration
object you pass in has the same shape as the type to decode, but you have to specify decoders for all the members instead. This ensures that your compile-time type definition and your run-time decoder definition will always stay in sync with one another.
The shape of the configuration
object is the same as the shape of your interface. If your JSON has a slightly different shape, you can pass a Decoder.field or an Decoder.fieldOpt as one of the parameters instead, ignoring the default mapping.
If your JSON structure and your target interface structure are vastly different, you might want to consider creating intermediate DTO interfaces instead, and using Decoder.map to build your final objects.
Example:
interface Point {
x: number;
y: number;
}
// ok: configuration has the same shape and the same types as Point
const pointDecoder = Decoder.obj<Point>({
x: Decoder.float,
y: Decoder.float
})
// ok: configuration has the same shape as the Point interface, but x/y got renamed to u/v
const uvDecoder = Decoder.obj<Point>({
x: Decoder.field('u', Decoder.float),
y: Decoder.field('v', Decoder.float)
})
// compile error: configuration is missing a field
const pointDecoder2 = Decoder.obj<Point>({
x: Decoder.float
})
// compile error: configuration and interface types don't match
const pointDecoder3 = Decoder.obj<Point>({
x: Decoder.integer,
y: Decoder.integer
})
// compile error: configuration declares a non-optional field as optional
const pointDecoder4 = Decoder.obj<Point>({
x: Decoder.integer,
y: Decoder.integer.optional()
})
Parameters:
| Param | Type |
| ------ | ------ |
| configuration | DecoderTable
<T
> |
Returns: Decoder<T
>
<Static>
dict
▸ dict<TDict
,TValue
>(valueDecoder: Decoder<TValue
>, explicitConfiguration: DecoderTable
<Explicit
<TDict
>>): Decoder<TDict
>
Create a decoder for types with an index signature. Because index signatures can optionally have required keys, this function not only takes a default valueDecoder
, but also an explicitConfiguration
map in the same format as the one for Decoder.obj, allowing for explicit control over those fields.
Example:
type ComplexKey = { key: string, ctrl?: boolean, alt?: boolean, shift?: boolean };
type Key = string | ComplexKey;
type Keymap = {
[command: string]: Key,
// editing keymap needs a keybinding, to prevent the user from locking themselfes out
"settings.editKeymap": Key
};
const keyDecoder =
Decoder.string.orElse(
Decoder.obj<ComplexKey>({
key: Decoder.string,
ctrl: Decoder.optional(Decoder.boolean),
alt: Decoder.optional(Decoder.boolean),
shift: Decoder.optional(Decoder.boolean)
}));
const keymapDecoder = Decoder.dict<Keymap>(keyDecoder, {
"settings.editKeymap": keyDecoder
});
Parameters:
| Param | Type |
| ------ | ------ |
| valueDecoder | Decoder<TValue
> |
| explicitConfiguration | DecoderTable
<Explicit
<TDict
>> |
Returns: Decoder<TDict
>
<Static>
tuple
▸ tuple<Args
>(...decoders: DecoderTuple
<Args
>): Decoder<Args
>
Decode a tuple of known parameter types.
const keyValueDecoder = Decoder.tuple<[string, string]>(Decoder.string, Decoder.string);
keyValueDecoder.decodeValue(["msg", "Hello, Sailor!"]); // OK
keyValueDecoder.decodeValue(["msg"]) // Err - second tuple argument missing
keyValueDecoder.decodeValue({0: "msg", 1: "Hello, Sailor!"}) // Err - not an array
While Decoder.optional works for allowing null
- values inside the tuple, it will NOT accept non-existent tuple elements. Similar to optional function paramters, this could lead to confusing results once optional and required values are mixed.
Instead, I recommend using Decoder.oneOf or Decoder.andThen to dispatch to multiple different decoders for each number of values.
Parameters:
| Param | Type |
| ------ | ------ |
| Rest
decoders | DecoderTuple
<Args
> |
Returns: Decoder<Args
>
<Static>
array
▸ array<T
>(itemDecoder: Decoder<T
>): Decoder<T
[]>
Convert a regular decoder into a decoder for arrays of that type. Array decoders only succeed if all items can be successfully decoded. If this is not your desired behaviour, you can use Decoder.orDefaultValue and Decoder.map to return default values and (optionally) filter those out instead.
Example:
const d1 = Decoder.array(Decoder.int);
d1.decodeValue([3, 4, 5]) // ==> [3, 4, 5]
d1.decodeValue([3, "4", 5]) // ==> DecoderError ...
d1.decodeValue([]) // ==> []
d1.decodeValue({0: 3, 1: 4, 2: 5, length: 3}) // ==> DecoderError ...
Parameters:
| Param | Type |
| ------ | ------ |
| itemDecoder | Decoder<T
> |
Returns: Decoder<T
[]>
<Static>
class
▸ class<T
,Args
>(constructor: object
, ...argsDecoders: DecoderTuple
<Args
>): Decoder<T
>
Create a decoder that extracts values to pass to a class constructor.
Example:
class Point {
constructor(
public readonly x: number,
public readonly y: number
) { }
}
const pointDecoder = Decoder.class(Point,
Decoder.field('x', Decoder.int),
Decoder.field('y', Decoder.int));
Parameters:
| Param | Type | Description |
| ------ | ------ | ------ |
| constructor | object
| A class constructor to use to instanciate the resulting object. |
| Rest
argsDecoders | DecoderTuple
<Args
> | A decoder for each parameter of the constructor. |
Returns: Decoder<T
>
<Static>
function
▸ function<T
,Args
>(constructor: function
, ...argsDecoders: DecoderTuple
<Args
>): Decoder<T
>
Create a decoder that extracts values to pass to a value-constructing function. This is very similar to a static version of Decoder.map that supports multiple arguments.
Parameters:
| Param | Type | Description |
| ------ | ------ | ------ |
| constructor | function
| A function to call to instanciate the resulting object. |
| Rest
argsDecoders | DecoderTuple
<Args
> | A decoder for each parameter of the function. |
Returns: Decoder<T
>
Advanced objects and tuples
<Static>
tag
▸ tag<T
>(value: T
): Decoder<T
>
Creates a Decoder that checks for some hardcoded primitive value, as used in literal types or discriminated unions. In contrast to the succeed
decoder, this one does not always return the same value, but only accepts excatly one value as the only valid one.
Parameters:
| Param | Type |
| ------ | ------ |
| value | T
|
Returns: Decoder<T
>
<Static>
optional
▸ optional<T
>(decoder: Decoder<T
>, defaultValue?: T): OptionalDecoder
<T
>
Makes a decoder that doesn't fail on null
or undefined
, but instead returns null, or a custom value.
You may notice that this function returns an OptionalDecoder<T>
instead of your regular Decoder<T>
. This is a hack used to enable typechecks on optional fields, both classes are practially and semantically identical.
Also note that this library works best with strictNullChecks enabled. If you don't use this option, every value can potentially be null, so every decoder has to handle null values at some point, which makes everything safe, but is not very pleasent to work with.
Example:
// This assumes strictNullChecks
interface Point {
x: number,
y: number,
z?: number
}
// Error TS2345: Argument of type { ... } is not assignable to parameter of type 'DecoderTable<Point>'.
// Types of property 'z' are incompatible.
// Type 'Decoder<number>' is not assignable to type 'OptionalDecoder<number|undefined>'.
// Property '__you_tried_to_use_a_non_optional_Decoder_on_an_optional_field__' is missing in type 'Decoder<number>'.
const pointDecoder = Decoder.obj<Point>({
x: Decoder.float,
y: Decoder.float,
z: Decoder.float
});
// OK - Decoder.optional returns the right type
const pointDecoder = Decoder.obj<Point>({
x: Decoder.float,
y: Decoder.float,
z: Decoder.optional(Decoder.float)
});
Parameters:
| Param | Type |
| ------ | ------ |
| decoder | Decoder<T
> |
| Optional
defaultValue | T |
Returns: OptionalDecoder
<T
>
<Static>
lazy
▸ lazy<T
>(decoder: function
): Decoder<T
>
Lazy makes working with recursive data structures, like nested comments, less painfull.
In Typescript, you cannot use a const
or let
value before it is assigned. Wrapping the decoder inside a Decoder.lazy(() => decoder)
will work around that, but Typescript will still complain to you that now the type of decoder
cannot be inferred, because it is recursively used in its own definition, so we also need an additional type annotation.
Please also make sure to never wrap Decoder.field
or Decoder.fieldOpt
directly inside of lazy. Because of laziness, the Decoder doesn't know yet wether or not it decodes a field or not, so dispatching to the field decoder will not work correctly. Instead, you can wrap the fieldDecoder
inside.
Example:
interface Comment {
message: string;
responses: Comment[]
}
const commentDecoder = Decoder.obj<Comment>({
message: Decoder.string,
// Important: Wrap the Decoder.array instead of the Decoder.field for the remapping of the properties to work!
responses: Decoder.field('replies', Decoder.lazy(() => Decoder.array(commentDecoder)))
});
Parameters:
| Param | Type |
| ------ | ------ |
| decoder | function
|
Returns: Decoder<T
>
<Static>
lazyOpt
▸ lazyOpt<T
>(decoder: function
): OptionalDecoder
<T
>
Parameters:
| Param | Type |
| ------ | ------ |
| decoder | function
|
Returns: OptionalDecoder
<T
>
<Static>
field
▸ field<T
>(field: string
, fieldDecoder: Decoder<T
>): Decoder<T
>
Decodes a single field of an object using a fieldDecoder
, or fail if the value is not an object, or the field doesn't exist.
Note that this is meaningfully different of a field that has a value of null
, which the fieldDecoder
might want to handle seperately.
This function usually get's called automatically by Decoder.obj, but you might find it usefull for decoding a single field type, or for changing the structure in an object decoder.
Parameters:
| Param | Type |
| ------ | ------ |
| field | string
|
| fieldDecoder | Decoder<T
> |
Returns: Decoder<T
>
<Static>
fieldOpt
▸ fieldOpt<T
>(field: string
, fieldDecoder: Decoder< T
| null
>, defaultValue?: T): OptionalDecoder
<T
>
Decode a single optional field of an object using the fieldDecoder
. In contrast to Decoder.field, this decoder will not immediately fail if the field does not exist, but instead relay a null
or defaultValue
to the underlying fieldDecoder
, if that decoder is setup to work on optional values.
To ensure decoders can handle optional values, it is usually best to wrap them inside of Decoder.optional last.
Remember that a field being optional is different from a value being optional! This function deals with the former case, which are those cases where the field is actually missing. The value of a field that is not missing can still be null
though, which is handled by Decoder.optional.
Example:
const optionalRevisionDecoder = Decoder.fieldOpt('rev', Decoder.optional(Decoder.int), '')
Parameters:
| Param | Type |
| ------ | ------ |
| field | string
|
| fieldDecoder | Decoder< T
| null
> |
| Optional
defaultValue | T |
Returns: OptionalDecoder
<T
>
<Static>
index
▸ index<T
>(index: number
, indexDecoder: Decoder<T
>): Decoder<T
>
Decode a single entry of an array using the indexDecoder
. This is similar to Decoder.field, but for arrays. Since this function checks to see if the input is actually a real array, please use that function instead if your object just happens to have a numeric key.
This function usually gets called automatically by Decoder.tuple, but you may find it usefull for reordering tuple elements.
Example:
type Pair = [number, number];
const reverseTupleDecoder = Decoder.tuple<Pair>(
Decoder.index(1, Decoder.int),
Decoder.index(0, Decoder.int));
reverseTupleDecoder.decodeValue([3, 4]) // OK [4, 3]
Parameters:
| Param | Type |
| ------ | ------ |
| index | number
|
| indexDecoder | Decoder<T
> |
Returns: Decoder<T
>
<Static>
guard
▸ guard<T
>(guard: (value: any) => value is T
, message: string
): Decoder<T
>
Make a completely custom Decoder based on a type guard.
Mapping results
<Static>
succeed
▸ succeed<T
>(value: T
): Decoder<T
>
Ignore the JSON and always return the given value instead. This is useful when used together with oneOf
or andThen
.
Parameters:
| Param | Type |
| ------ | ------ |
| value | T
|
Returns: Decoder<T
>
<Static>
succeedOpt
▸ succeedOpt<T
>(value?: T
): OptionalDecoder<T
>
Decoder.succeed
variant for optional fields.
Ignore the JSON and always return the given value instead. This is useful when used together with oneOf
or andThen
.
<Static>
fail
▸ fail<T
>(error: string
): Decoder<T
>
Ignore the JSON and always make the decoder fail with a custom error message. This is useful when used together with oneOf
or andThen
.
Parameters:
| Param | Type |
| ------ | ------ |
| error | string
|
Returns: Decoder<T
>
map
▸ map<U
>(transformer: function
): Decoder<U
>
Additionally map the decoded value to some other type after successfully decoding it.
Parameters:
| Param | Type |
| ------ | ------ |
| transformer | function
|
Returns: Decoder<U
>
filter
▸ filter(message: string
, predicate: function
): Decoder<T
>
After successfully decoding a value, additionally validate the resulting value using a predicate
function. The decoder will also fail with message
if this predicate returns false.
Example:
const pairDecoder = Decoder.array(Decoder.int)
.filter("Expected a tuple [x, y] of exactly 2 elements!", value => value.length == 2)
pairDecoder.decodeString("[4, 3]") // Ok - [3,4]
pairDecoder.decodeString("[4, 3, 2]") // Error
Parameters:
| Param | Type |
| ------ | ------ |
| message | string
|
| predicate | function
|
Returns: Decoder<T
>
filterOptional
▸ filterOptional<R
>(this: Decoder<R | null | undefined
>, message: string
): Decoder<R
>
Filter out null
and undefined
values.
This returns a new Decoder that fails instead with the supplied message if it encounters one of those values.
The returned Decoder no longer contains those values as possible success values, making this useful in combination with Decoder.function
if you need to wrap a function returning null on a failure.
Parameters:
| Param | Type |
| ------ | ------ |
| message | string
|
Returns: Decoder<R
>
filterGuard
▸ filterGuard<R
>(guard: (value: T | R
) => value is R
, message: string
): Decoder<T & R
>
Filter out certain types of values using a type guard function.
orDefaultValue
▸ orDefaultValue(defaultValue: T
): Decoder<T
>
Instead of failing, make a new decoder that instead returns a defaultValue
if something goes wrong.
Example:
const int = Decoder.int;
const intOrDefault = int.orDefaultValue(0);
int.decodeValue(5) // ==> 5
int.decodeValue(3.14) // ==> DecoderError
intOrDefault.decodeValue(5) // ==> 5
intOrDefault.decodeValue(3.14) // ==> 0
Parameters:
| Param | Type |
| ------ | ------ |
| defaultValue | T
|
Returns: Decoder<T
>
orElse
▸ orElse<U
>(alternative: Decoder<U
>): Decoder< T
| U
>
Tries an alternative decoder on the same input if this one fails for some reason.
If you are building a big orElse
chain, please note that if something goes wrong, the next decoder in the chain will always be tried, even though the current would "fit better". This is especially bad, because in a orElse chain, only the last decoder's errors will be returned. If you are decoding a discriminated union of some sort, consider switching to Decoder.andThen instead for better error messages and performance.
see: Decoder.oneOf
Example:
interface Success<T> {
value: T;
success: true;
}
interface Failure {
error: string;
success: false;
}
type Result<T> = Success<T> | Failure;
const successDecoder = <T> (valueDecoder: Decoder<T>) => Decoder.obj<Success<T>>({
value: valueDecoder,
success: Decoder.tag(true)
});
const failureDecoder = Decoder.obj<Failure>({
error: Decoder.string,
success: Decoder.tag(false)
});
const resultDecoder = <T> (valueDecoder: Decoder<T>): Decoder<Result<T>> =>
failureDecoder.orElse(successDecoder(valueDecoder));
Parameters:
| Param | Type |
| ------ | ------ |
| alternative | Decoder<U
> |
Returns: Decoder< T
| U
>
<Static>
oneOf
▸ oneOf<T
>(...decoders: Array
<Decoder<T
>>): Decoder<T
>
Try all decoders in order until one of them succeeds. Note that because the next decoder will be tried if something goes wrong, only the error messages of the last decoder will be returned if all of them fail.
If you are dealing with versioned data or discriminated unions, consider switching to Decoder.andThen instead, to improve error messages and performance. see: Decoder.orElse
Parameters:
| Param | Type |
| ------ | ------ |
| Rest
decoders | Array
<Decoder<T
>> |
Returns: Decoder<T
>
andThen
▸ andThen<U
>(continuation: function
): Decoder<U
>
Runs a second Decoder on the same input as this
one, but only if the first one succeeded. Using this, you can first parse a small part of a larger JSON structure to determine the actual Decoder to use. Possible applications might be versioned data, JSON with dynamic types, or tagged unions.
Example:
const configDecoder = Decoder.field('version', Decoder.int).andThen(version => {
switch(version) {
case 3: return config3Decoder;
case 4: return config4Decoder;
default: return Decoder.fail("Only Versions 3 and 4 are supported!");
}
})
Parameters:
| Param | Type |
| ------ | ------ |
| continuation | function
|
Returns: Decoder<U
>
fold
▸ fold<U
>(Left: function
, Right: function
): Decoder<U
>
Use an 'Either' type to pull errors into a successful value, making the Decoder no longer fail if something goes wrong, but instead call the Right
constructor on the value.
Parameters:
| Param | Type | Description |
| ------ | ------ | ------ |
| Left | function
| 'Either' object constructor for the Failure case |
| Right | function
| 'Either' object constructor for the Success case |
Returns: Decoder<U
>