Type-safe parsers for unknown data using neverthrow Result types.
Every parser takes an unknown input and returns a Result<T, ParseError> — no exceptions, just values.
npm install neverthrow-parse neverthrowimport { string, number, shape, arrayOf } from 'neverthrow-parse'
// Primitive parsing
const name = string('hello') // Ok<string>
const age = number('oops') // Err<{ type: 'input_is_not_a_number', input: 'oops' }>
// Object shapes
const user = shape(json, {
name: string,
age: number,
})
// Ok<{ name: string, age: number }> or Err with structured error
// Arrays
const ids = arrayOf(json, number)
// Ok<number[]> or Err with index of failing itemReturns Ok<string> or Err<StringParseError>.
Returns Ok<number> or Err<NumberParseError>. Rejects NaN.
Returns Ok<bigint> or Err<BigintParseError>.
Parses a JSON string. Returns Ok<JsonValue> or Err<JsonParseError>. Accepts an optional reviver function, same as JSON.parse.
string(input).andThen(json)
// Ok<JsonValue> or Err<StringParseError | JsonParseError>Validates that input is a non-null, non-array object. Returns Ok<Record<PropertyKey, unknown>> or Err<ObjectParseError>.
Parses an array where every item is validated by itemParser. On failure, the error includes the index of the first failing item.
arrayOf([1, 2, 'x'], number)
// Err<{ type: 'item_parse_error', index: 2, error: { type: 'input_is_not_a_number', ... } }>Parses an object as a typed record, validating both keys and values.
recordOf(json, { key: string, value: number })
// Ok<Record<string, number>>Parses an object against a schema of named parsers. Infers the output type from the schema.
const result = shape(json, {
host: string,
port: number,
})
// Result<{ host: string, port: number }, ShapeParseError<...>>Errors include context: missing_key (with key name) or value_parse_error (with key name and nested error).
Every parser exports its error type (e.g. StringParseError, ShapeParseError<E>). All errors are discriminated unions with a type field, making them easy to match:
const result = number(input)
if (result.isErr()) {
switch (result.error.type) {
case 'input_is_not_a_number':
// result.error.input is the original value
break
case 'input_is_nan':
break
}
}