can1357/oh-my-pi · error · OmpTypeError

${parameter.name} must be assignable to its constraint

Error message

${parameter.name} must be assignable to its constraint

What it means

When instantiating a generic with constraint declarations, each argument is checked with isSubtype against its parameter's constraint; a violating argument throws OmpTypeError. Constraints restrict what type arguments are admissible, mirroring TypeScript's 'T extends X'.

Source

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

	validateGenericParameters(parameters);
	const placeholders = parameters.map(parameter =>
		parameter.constraintDef === undefined
			? ({ k: "unknown" } as IR)
			: parseDef(parameter.constraintDef, constraintResolve),
	);
	if (validateBody) genericBodyIR(parameters, definition, placeholders, outer);
	const meta: GenericMeta = {
		parameters,
		instantiateIR(arguments_) {
			if (arguments_.length !== parameters.length) {
				throw new OmpTypeError(`generic expects ${parameters.length} arguments (received ${arguments_.length})`);
			}
			for (let index = 0; index < parameters.length; index++) {
				const parameter = parameters[index];
				if (parameter.constraintDef === undefined) continue;
				const constraint = parseDef(parameter.constraintDef, constraintResolve);
				if (!isSubtype(arguments_[index], constraint)) {
					throw new OmpTypeError(`${parameter.name} must be assignable to its constraint`);
				}
			}
			return genericBodyIR(parameters, definition, arguments_, outer);
		},
	};
	const generic = Object.assign(
		(...arguments_: readonly unknown[]) =>
			makeType(
				meta.instantiateIR(arguments_.map(argument => parseGenericArgument(argument, outer))),
				EMPTY_STEPS,
				EMPTY_META,
			),
		{ [GENERIC_META]: meta },
	);
	Object.defineProperty(generic, GENERIC_META, { value: meta });
	return generic;
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Pass a type argument that satisfies the constraint (e.g. 'number' or a numeric literal for 'T extends number')
  2. Verify argument order matches parameter order in multi-parameter generics
  3. If the constraint is too strict for your use case, widen the declared constraint
  4. Pre-validate with isSubtype(argument, constraint) yourself before instantiating to give a better message

Example fix

// before
const G = generic('<T extends number>', 'T[]');
const a = G('string'); // throws
// after
const a = G('number');
Defensive patterns

Strategy: validation

Validate before calling

import { isSubtype, type } from '@oh-my-pi/omptype';
function assertSatisfies(arg: unknown, constraintDef: string, name: string) {
  const constraint = type(constraintDef);
  if (!isSubtype(arg, constraint)) {
    throw new Error(`${name} must satisfy constraint ${constraintDef}`);
  }
}

Try / catch

try {
  const inst = G(arg);
} catch (err) {
  if (err instanceof OmpTypeError && err.message.includes('must be assignable to its constraint')) {
    throw new Error(`Argument for ${err.message.split(' ')[0]} violates its constraint — widen the constraint or pass a narrower type`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Instantiating a constrained generic with a type that isn't assignable to the constraint, e.g. generic('<T extends number>', 'T[]') instantiated with 'string'; the constraintDef is parsed and isSubtype(argument, constraint) returns false.

Common situations: Passing a broader type (string) where a narrower one (a literal union or number) is constrained; swapping argument order in a multi-parameter generic so the wrong argument meets the wrong constraint; loosening the body but not the constraint (or vice versa) during refactors.

Related errors


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