A declarative, composable library of JSON compatible value parsers. Unminimized, uncompressed payload of 4703 bytes.
npm install parseroni
The gist: Parser<I,O> is a function that takes a type I in as its single argument and returns a Result<I,O>.
Result<I,O> is a union of Success<O>|Failure<I> which are container types that describe the success and failure branches of the Parser<I,O> logic.
This library was designed to be used with values returned from JSON.parse.
A basic JSON parser:
constparseJSON: Parser<any,any>=(input)=>{try{returnsuccess(JSON.parse(input));}catch(error){returnfailure(error.message,input);}}Parser<any,any> is not exactly useful when the goal is type safety. This library provides the building blocks to validate and return JSON primitives, and combinators to compose more complex validation.
A set of functions to simplify working with Result<I,O> types.
isSuccess is a guard function that refines a Result<I,O> to Success<O>.
constresult=parseString(1);if(isSuccess(result)){constvalue=result.value;// value _is_ a `string`}isFailure is the logical inverse of isSuccess and refines the Failure<I> branch of a Result<I, O>:
constresult=parseString('maybe a string');if(isFailure(result)){thrownewError(result.message);}value unwraps the boxed value of Success<T>. Allows the shape of Success<T> to be opaque to the user of this API.
constresult=parseString('maybe a string');if(isSuccess(result)){consttheString=value(result);}Bulids a Success<T> result case:
constparseInteger: Parser<any,number>=(maybeNumber)=>{constint=parseInt(maybeNumber);returnint===maybeNumber
? success(int)
: failure(`${maybeNumber} not an int`,maybeNumber);}Builds a Failure<T> result case:
constparsePositive: Parser<any,number>=(maybeNumber)=>{returntypeofmaybeNumber==='number'&&maybeNumber>0
? success(maybeNumber)
: failure(`${maybeNumber} not a posistive number`,maybeNumber);}A set of parsers for non-container JSON literals that can be used to build more complex parsers.
parseStringisParser<any, string>parseNumberis `Parser<any, number>parseUndefinedisParser<any, undefined>parseNullisParser<any, null>parseBooleanisParser<any, boolean>
constresult=parseString(("maybe a string": any));if(isFailure(result)){thrownewError(result.reason);}conststrValue=value(result);Combinators that combine parsers into more complex parsers.
parseObjectOf accepts key/value pairs of parsers and parses the key/value pairs of the value it receives.
constparsePerson=parseObjectOf({name: parseString,age: parseNumber,metInPerson: parseBoolean});constresult=parsePerson({});if(isFailure(result)){thrownewError(result.reason);}constperson=value(result);// TypeScript knows person.name is a `string`.console.log(`Hello ${person.name}`);// TypeScript knows person.age is a `number`.console.log(`Maybe born in`,(newDate()).getYear()-person.age);For parsing values of type Array<T>.
Given any Parser<I, O>, succeeds when the input:
- is an
Array - each member of the
Arraysucceeds the providerParser<I, O>
parseArrayOf accepts a Parser<I,O> and returns a Parser<I, Array<O>>.
constparsePeople=parseArrayOf(parsePerson);// Result<any, Array<{name: string, age: number, metInPerson: boolean}>>constresult=parsePeople(JSON.parse(someString));For parsing values of type {[string]: T}.
Given a Parser<any, T> the returned parser succeeds when:
- The value is an indexed object
- Each member of the indexed object succeeds the provider
Parser<any, T>
// An object that is an index of users, indexed by a string valueconstparseUuser=parseObjectOf({username: parseString});constparseUserIndex=parseIndexedObjectOf(parseUser);Builds a parser that succeeds when the input is exactly equal to the provided value.
constparse=parseExactly('shipped');constresult=parse('pending');if(isSuccess(result)){constshipped: 'shipped'=result.value;}Allows one of a list of parsers to succeed. Useful for parsing an enumeration of known values.
constparseStatus=parseOneOf(parseExactly('published'),parseExactly('draft'));// Result<any, 'published'|'draft'>constresult=parseStatus('other');Given any parser, returns a new parser that succeeds when original parser succeeds or the value is null.
constparse=optional(parseString);// Result<any, (null|string)>constresult=parse(null);Given any parser, returns a new parser that succeeds when the original parser succeeds or the value is undefined.
constparse=voidable(parseString);// Result<any, (undefined|string)>constresult=parse(undefined);import{ParserType,parseObjectOf,parseArrayOf,parseString,parseExactly,optional,mapParser,success,isSuccess,}from'parser';constparseAuthor=parseObjectOf({username: parseString,avatar: optional(parseString),});constparsePost=parseObjectOf(title: parseString,status: parseOneOf(parseExactly('published'),parseExactly('draft'),),author: parseAuthor,// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parsepublishedAt: mapParser(parseString,// assuming ISO8601 stringstring=>success(newDate(string))),);/** * Expects JSON like: * * ``` * {"posts": [...Post]} * ``` */constparseResponse=parseObjectOf({posts: parseArrayOf(parsePost)})/** * Chained with DOM fetch */asyncfunctiongetPosts(){constresponse=awaitfetch('/api/site/awesome.blog/posts').then(response=>response.json()).then(parseResponse);/** * Type guard to unwrap the parsed value */if(isSuccess(response)){// 🚀 The response was successfully parsed and is safely typedconstpostResponse=response.value;console.log(postResponse.posts);return;}thrownewError(response.reason);}// Use the types created by the parsers:typeAuthor=ParserType<typeofparseAuthor>;typePost=ParserType<typeofparsePost>;constauthor: Author={username: 5,// 💥 Not a string!};// 💥 no `author`constpost: Post={title: 'Hello World',status: 'other',// 💥 Not 'published' or 'draft'publishedAt: 1235500482,// 💥 Not a Date};