Skip to content

Stringent

One grammar definition, two engines — expressions in string literals are validated and fully typed at compile time, while dynamic strings get structured errors and runtime evaluation.
const
const schema: {
readonly values: {
readonly password: "string";
readonly confirmPassword: "string";
};
}
schema
= {
values: {
readonly password: "string";
readonly confirmPassword: "string";
}
values
: {
password: "string"
password
: "string",
confirmPassword: "string"
confirmPassword
: "string" } } as
type const = {
readonly values: {
readonly password: "string";
readonly confirmPassword: "string";
};
}
const
;
const
const values: {
values: {
password: string;
confirmPassword: string;
};
}
values
= {
values: {
password: string;
confirmPassword: string;
}
values
: {
password: string
password
: "hunter2",
confirmPassword: string
confirmPassword
: "hunter2" } };
const result =
const parser: Parser<[[NodeSchema<"eq", readonly [NamedSchema<ExprSchema<undefined, "operand">, "left">, ConstSchema<readonly ["=="]>, NamedSchema<ExprSchema<OverlapsRef<"left">, "rest">, "right">], 1, "boolean">], [NodeSchema<"num", readonly [NumberSchema], 2, undefined>, NodeSchema<"str", readonly [StringSchema<readonly ["\"", "'"]>], 2, undefined>, NodeSchema<"var", readonly [PathSchema], 2, undefined>]], readonly [...], {}>
parser
.
Parser<[[NodeSchema<"eq", readonly [NamedSchema<ExprSchema<undefined, "operand">, "left">, ConstSchema<readonly ["=="]>, NamedSchema<ExprSchema<OverlapsRef<"left">, "rest">, "right">], 1, "boolean">], [...]], readonly [...], {}>.evaluate<"values.password == values.confirmPassword", {
readonly values: {
readonly password: "string";
readonly confirmPassword: "string";
};
}>(input: "values.password == values.confirmPassword", schema: validateObjectLiteral<{
readonly values: {
readonly password: "string";
readonly confirmPassword: "string";
};
}, {}, bindThis<{
readonly values: {
readonly password: "string";
readonly confirmPassword: "string";
};
}>>, values: NoInfer<{
values: {
password: string;
confirmPassword: string;
};
}>): boolean

Parse a string literal and evaluate it against runtime values.

Node eval() functions (from defineNode) compute the result. The values object is validated against the schema before evaluation.

evaluate
("values.password == values.confirmPassword",
const schema: {
readonly values: {
readonly password: "string";
readonly confirmPassword: "string";
};
}
schema
,
const values: {
values: {
password: string;
confirmPassword: string;
};
}
values
);
const result: boolean

Hover the code above. Every type on this page is real — produced by the actual library, at documentation build time, from a grammar you define.

You declare each rule once — as data. The same declaration drives a type-level parser that checks string literals in your code, and a runtime parser that checks strings from users and config files.

Result types are inferred through the grammar — the same overloaded + yields number or string depending on its operands, and expressions that don’t parse don’t compile:

const seven =
const parser: Parser<[[NodeSchema<"ternary", readonly [NamedSchema<ExprSchema<"boolean", "operand">, "cond">, ConstSchema<readonly ["?"]>, NamedSchema<ExprSchema<undefined, "expr">, "then">, ConstSchema<readonly [":"]>, NamedSchema<ExprSchema<"then", "rest">, "else">], 0, "then">], [NodeSchema<"eq", readonly [NamedSchema<ExprSchema<undefined, "operand">, "left">, ConstSchema<readonly ["=="]>, NamedSchema<ExprSchema<OverlapsRef<"left">, "rest">, "right">], 1, "boolean">], [...], [...], [...]], readonly [...], {}>
parser
.
Parser<[[NodeSchema<"ternary", readonly [NamedSchema<ExprSchema<"boolean", "operand">, "cond">, ConstSchema<readonly ["?"]>, NamedSchema<ExprSchema<undefined, "expr">, "then">, ConstSchema<...>, NamedSchema<...>], 0, "then">], [...], [...], [...], [...]], readonly [...], {}>.evaluate<"1+2*3", {}>(input: "1+2*3", schema: {}, values: object): number

Parse a string literal and evaluate it against runtime values.

Node eval() functions (from defineNode) compute the result. The values object is validated against the schema before evaluation.

evaluate
("1+2*3", {}, {});
const seven: number
const greeting =
const parser: Parser<[[NodeSchema<"ternary", readonly [NamedSchema<ExprSchema<"boolean", "operand">, "cond">, ConstSchema<readonly ["?"]>, NamedSchema<ExprSchema<undefined, "expr">, "then">, ConstSchema<readonly [":"]>, NamedSchema<ExprSchema<"then", "rest">, "else">], 0, "then">], [NodeSchema<"eq", readonly [NamedSchema<ExprSchema<undefined, "operand">, "left">, ConstSchema<readonly ["=="]>, NamedSchema<ExprSchema<OverlapsRef<"left">, "rest">, "right">], 1, "boolean">], [...], [...], [...]], readonly [...], {}>
parser
.
Parser<[[NodeSchema<"ternary", readonly [NamedSchema<ExprSchema<"boolean", "operand">, "cond">, ConstSchema<readonly ["?"]>, NamedSchema<ExprSchema<undefined, "expr">, "then">, ConstSchema<...>, NamedSchema<...>], 0, "then">], [...], [...], [...], [...]], readonly [...], {}>.evaluate<"'Hello '+name", {
readonly name: "string";
}>(input: "'Hello '+name", schema: validateObjectLiteral<{
readonly name: "string";
}, {}, bindThis<{
readonly name: "string";
}>>, values: NoInfer<{
name: string;
}>): string

Parse a string literal and evaluate it against runtime values.

Node eval() functions (from defineNode) compute the result. The values object is validated against the schema before evaluation.

evaluate
("'Hello '+name", {
name: "string"
name
: "string" }, {
name: string
name
: "Ada" });
const greeting: string
const label =
const parser: Parser<[[NodeSchema<"ternary", readonly [NamedSchema<ExprSchema<"boolean", "operand">, "cond">, ConstSchema<readonly ["?"]>, NamedSchema<ExprSchema<undefined, "expr">, "then">, ConstSchema<readonly [":"]>, NamedSchema<ExprSchema<"then", "rest">, "else">], 0, "then">], [NodeSchema<"eq", readonly [NamedSchema<ExprSchema<undefined, "operand">, "left">, ConstSchema<readonly ["=="]>, NamedSchema<ExprSchema<OverlapsRef<"left">, "rest">, "right">], 1, "boolean">], [...], [...], [...]], readonly [...], {}>
parser
.
Parser<[[NodeSchema<"ternary", readonly [NamedSchema<ExprSchema<"boolean", "operand">, "cond">, ConstSchema<readonly ["?"]>, NamedSchema<ExprSchema<undefined, "expr">, "then">, ConstSchema<...>, NamedSchema<...>], 0, "then">], [...], [...], [...], [...]], readonly [...], {}>.evaluate<"1==2 ? 'yes' : 'no'", {}>(input: "1==2 ? 'yes' : 'no'", schema: {}, values: object): string

Parse a string literal and evaluate it against runtime values.

Node eval() functions (from defineNode) compute the result. The values object is validated against the schema before evaluation.

evaluate
("1==2 ? 'yes' : 'no'", {}, {});
const label: string
// ...and an expression that doesn't parse doesn't compile:
const parser: Parser<[[NodeSchema<"ternary", readonly [NamedSchema<ExprSchema<"boolean", "operand">, "cond">, ConstSchema<readonly ["?"]>, NamedSchema<ExprSchema<undefined, "expr">, "then">, ConstSchema<readonly [":"]>, NamedSchema<ExprSchema<"then", "rest">, "else">], 0, "then">], [NodeSchema<"eq", readonly [NamedSchema<ExprSchema<undefined, "operand">, "left">, ConstSchema<readonly ["=="]>, NamedSchema<ExprSchema<OverlapsRef<"left">, "rest">, "right">], 1, "boolean">], [...], [...], [...]], readonly [...], {}>
parser
.
Parser<[[NodeSchema<"ternary", readonly [NamedSchema<ExprSchema<"boolean", "operand">, "cond">, ConstSchema<readonly ["?"]>, NamedSchema<ExprSchema<undefined, "expr">, "then">, ConstSchema<...>, NamedSchema<...>], 0, "then">], [...], [...], [...], [...]], readonly [...], {}>.parse<"1+'a'", {}>(input: never, schema: {}): [NumberNode<"1">, "+'a'"]

Parse a string literal, validated at compile time.

The input must be a literal that FULLY parses against the grammar — anything else is a compile-time error. For runtime-provided strings, use safeParse. Throws StringentParseError if the compile-time check was bypassed and the input is invalid.

parse
("1+'a'", {});
Error ts(2345) ― Argument of type '"1+'a'"' is not assignable to parameter of type 'never'.

Compile-time validation

parse() only accepts string literals that fully parse against your grammar. An invalid expression — a syntax error or an operand type mismatch — is a compile-time error, and valid expressions get an exactly inferred AST and result type. safeParse() even catches schema typos at the offending leaf.

Runtime safety

Dynamic strings go through safeParse(), which never throws for input. You get structured errors with an error code, a 0-based position, and the tokens that would have been valid there.

Polymorphic grammars

Constraints are arktype definitions and matching is assignability. Union constraints, binding references (operand("left"), rest(overlapping("left"))) and derived result types (resultType: "left") give you overloaded operators without per-type node variants — one add node handles number + number → number and string + string → string.

Rules as Standard Schemas

parser.compile() turns a rule into a real arktype Type — a boolean rule becomes a validating predicate with field-attributed ArkErrors, so it drops straight into react-hook-form, tRPC, or hono.

Secure by default

Expressions are treated as untrusted input. All identifier and path lookups are own-property only — __proto__, constructor and friends never resolve to prototype internals, at parse time or eval time.