Skill v1.0.0
currentAutomated scan100/100version: "1.0.0" name: tsentials-json description: Use when parsing or stringifying JSON safely without exceptions — safeJsonParse/safeJsonStringify return Result<T> instead of throwing, parseAndValidate combines parsing with a custom type guard, and Json/JsonObject/JsonArray/JsonPrimitive types plus isJson/isJsonObject guards enforce structural validity at compile and runtime.
tsentials/json
Type-safe JSON parsing and validation that returns Result<T> — no exceptions, no try/catch, fits directly into the railway pipeline.
Installation
npm install tsentials
Import
import { safeJsonParse, safeJsonStringify, parseAndValidate } from 'tsentials/json';import { isJson, isJsonObject, isJsonArray, isJsonPrimitive } from 'tsentials/json';import type { Json, JsonObject, JsonArray, JsonPrimitive } from 'tsentials/json';
safeJsonParse
Parses a JSON string and returns Result<Json>. Never throws.
const result = safeJsonParse('{"name":"Alice","age":30}');if (result.ok) {console.log(result.value); // { name: "Alice", age: 30 }} else {console.error(result.errors[0].code);// "Json.SyntaxError" — malformed input// "Json.ValidationError" — parsed but contains undefined/functions/class instances}
safeJsonStringify
Stringifies a Json value and returns Result<string>. Catches circular reference errors.
const result = safeJsonStringify({ id: 1, tags: ['a', 'b'] });if (result.ok) {console.log(result.value); // '{"id":1,"tags":["a","b"]}'} else {console.error(result.errors[0].code); // "Json.StringifyFailed"}
parseAndValidate
Parses JSON and validates with a custom type guard. Returns Result<T> — typed to your domain type.
import { isJsonObject } from 'tsentials/json';interface User { name: string; age: number }function isUser(value: unknown): value is User {return isJsonObject(value) && typeof value.name === 'string' && typeof value.age === 'number';}const result = parseAndValidate<User>('{"name":"Alice","age":30}', isUser);if (result.ok) {console.log(result.value.name); // "Alice" — fully typed as User}
Type Guards
isJsonPrimitive('hello'); // true — string | number | boolean | nullisJsonPrimitive(undefined); // falseisJsonArray([1, 'a', null]); // trueisJsonArray({}); // falseisJsonObject({ key: 'value' }); // true — plain objects onlyisJsonObject(new Date()); // false — rejects class instancesisJsonObject([]); // false — arrays are not objectsisJson({ nested: [1, null] }); // true — recursive validationisJson({ fn: () => {} }); // false — functions not valid JSONisJson({ key: undefined }); // false — undefined not valid JSON
Important:isJsonObjectonly accepts plain objects (Object.prototypeornullprototype).new Date(),new Map(), class instances all returnfalse.
Error Codes
| Code | Cause | |
|---|---|---|
Json.SyntaxError | JSON.parse threw — malformed input string | |
Json.ValidationError | Parsed successfully but failed isJson or custom guard | |
Json.StringifyFailed | JSON.stringify threw — typically a circular reference |
Pipeline Integration
safeJsonParse returns Result<Json> so it chains directly into any railway pipeline:
import { Result, fromAsync } from 'tsentials/result';import { safeJsonParse, isJsonObject, parseAndValidate } from 'tsentials/json';// Chain into Result pipelineconst processed = Result.then(safeJsonParse(rawInput),data => validatePayload(data),);// Full async pipelineconst response = await fromAsync(fetchRawBody(req)).andThen(body => safeJsonParse(body)).andThen(data => parseAndValidate(JSON.stringify(data), isUser)).map(user => enrichUser(user)).match(user => ({ status: 200, body: user }),errs => ({ status: 400, body: errs[0]?.description }),);
Json Types
type JsonPrimitive = string | number | boolean | null;type JsonArray = readonly Json[];interface JsonObject { readonly [key: string]: Json | undefined }type Json = JsonPrimitive | JsonArray | JsonObject;
All types are readonly — immutability is enforced at compile time.
Best Practices
- Use
safeJsonParseinstead ofJSON.parse— eliminates uncaughtSyntaxErrorexceptions - Use
parseAndValidateto combine parsing and domain validation in one step - Combine with
Err.fromExceptiononly if wrapping legacy code that throws — prefersafeJsonParsenatively isJsonObjectis the right guard to use insideparseAndValidatetype guards — it rejects class instances thattypeof x === 'object'would accept- Do not use
Jsonas a domain type — parse to a specific interface withparseAndValidateinstead