can1357/oh-my-pi · error · OmpTypeError

cannot apply numeric bound to ${ir.k}

Error message

cannot apply numeric bound to ${ir.k}

What it means

withNumericBound() applies min/max bounds only to number IR nodes (or unions, by mapping each member). If the resolved IR kind is anything else — string, boolean, object, array, record, etc. — the library throws OmpTypeError because numeric bounds are meaningless there. This is a compile-time-mistake surfaced at runtime: you called .min()/.max() (or equivalent) on a type that isn't a number.

Source

Thrown at packages/omptype/src/type.ts:2474

	return Math.min(a, b);
}

function withLengthBound(ir: IR, side: "min" | "max", bound: number): IR {
	if (ir.k === "array" || ir.k === "string") {
		return side === "min" ? { ...ir, min: bound } : { ...ir, max: bound };
	}
	throw new OmpTypeError(`cannot apply length bound to ${ir.k}`);
}

function withNumericBound(ir: IR, side: "min" | "max", bound: number, exclusive = false): IR {
	if (!Number.isFinite(bound)) throw new OmpTypeError("numeric bound must be finite");
	if (ir.k === "number") {
		return side === "min" ? { ...ir, min: bound, xmin: exclusive } : { ...ir, max: bound, xmax: exclusive };
	}
	if (ir.k === "union") {
		return { ...ir, members: ir.members.map(member => withNumericBound(member, side, bound, exclusive)) };
	}
	throw new OmpTypeError(`cannot apply numeric bound to ${ir.k}`);
}
interface GenericParameter {
	readonly name: string;
	readonly constraintDef?: unknown;
}

interface GenericMeta {
	readonly parameters: readonly GenericParameter[];
	instantiateIR(arguments_: readonly IR[]): IR;
}

const GENERIC_META = Symbol("omptype.generic");

/** Callable runtime generic returned by `type("<t>", def)` and `type.generic(...)`. */
export type Generic = (...arguments_: readonly unknown[]) => BaseType;

interface RuntimeGeneric extends Generic {
	readonly [GENERIC_META]: GenericMeta;

View on GitHub (pinned to 9690622007)

Solutions

  1. Ensure the base definition is numeric, e.g. type('number').min(0) instead of type('string').min(0)
  2. If the type is a union, make every member numeric or bound each numeric member individually
  3. Use the correct constraint API for non-numeric kinds (string length, array length) instead of numeric bounds
  4. Catch OmpTypeError at schema-construction time and surface a clearer message to schema authors

Example fix

// before
const t = type('string').min(3); // throws
// after
const t = type('string').atLeastLength(3);
const n = type('number').min(3); // numeric bound on number is fine
Defensive patterns

Strategy: validation

Validate before calling

function assertNumericBounded(def: string) {
  if (!/^number\b|union<.*number.*>/.test(def.trim())) {
    throw new Error(`numeric bounds require a numeric definition, got: ${def}`);
  }
}

Type guard

function isNumberIR(ir: { k: string }): ir is { k: 'number'; min?: number; max?: number } {
  return ir.k === 'number' || ir.k === 'union';
}

Try / catch

try {
  const t = type(def).min(bound);
} catch (err) {
  if (err instanceof OmpTypeError && err.message.startsWith('cannot apply numeric bound')) {
    throw new Error(`Schema ${def} is not numeric; use length/range APIs instead`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling a numeric bound method (min/max style) on a type built from a non-numeric definition, e.g. type('string').min(...) via a path that goes through withNumericBound, or on a union containing non-numeric members.

Common situations: Refactoring a schema from number to string/integer-like and forgetting to drop the bound; applying bounds to a generic/aliased definition whose underlying kind isn't number; piping a user-supplied definition into a numeric-bound helper.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/6a94ea3aa73b54f7. Report an issue: GitHub.