{"record":{"id":"92d7039b6b2e2f7f","repo":"pydantic/monty","slug":"classtype-expects-a-class-constructor-function","errorCode":null,"errorMessage":"ClassType expects a class (constructor function)","messagePattern":"ClassType expects a class \\(constructor function\\)","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/monty-js/ts/classInstance.ts","lineNumber":285,"sourceCode":" *\n * ```ts\n * await session.feedRun('p = Point(1, 2)\\nassert p.x == 1', {\n *   inputs: { Point: new ClassType(Point, { init: true, instanceEagerAttrs: 'all' }) },\n * })\n * ```\n */\nexport class ClassType extends BaseWrapper {\n  /** The class's sandbox identity. Defaults to a process-wide id per class\n   *  object (every wrapper of one class agrees), so instances keep a stable\n   *  type identity; pass `id` to pin it explicitly, e.g. when restoring a\n   *  dump in a fresh process. */\n  readonly id: string\n\n  declare readonly options: ClassTypeOptions\n\n  constructor(classType: new (...args: never[]) => object, options: ClassTypeOptions = {}) {\n    if (typeof classType !== 'function') {\n      throw new TypeError('ClassType expects a class (constructor function)')\n    }\n    super(classType, options)\n    validatePolicy('instanceEagerAttrs', options.instanceEagerAttrs)\n    validatePolicy('instanceLazyAttrs', options.instanceLazyAttrs)\n    validatePolicy('instanceAllowedMethods', options.instanceAllowedMethods)\n    this.id = options.id === undefined ? classIdFor(classType) : normalizeId('ClassType', options.id)\n  }\n\n  /** The wrapped host class (the inherited `instance` field). */\n  get classType(): new (...args: never[]) => object {\n    return this.instance as new (...args: never[]) => object\n  }\n\n  /** Class name shown to the sandbox: `options.name`, else the class name. */\n  override getName(): string {\n    if (this.options.name !== undefined) {\n      return this.options.name\n    }","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty-js/ts/classInstance.ts#L267-L303","documentation":"ClassType wraps a host class so it can cross into the Monty sandbox (and optionally be instantiated there with init: true). Its constructor validates up front that the value is actually a class — a constructor function — and throws this TypeError for anything else (plain objects, class instances, primitives, arrow functions without constructor semantics used as classes).","triggerScenarios":"`new ClassType(point)` passing an instance instead of the class; `new ClassType(configObject)` passing a plain object; `new ClassType(null/undefined)`; passing something read out of a loosely typed registry or JSON config where the class was expected.","commonSituations":"Confusing the class with its instance (Point vs new Point()); importing a default export that is an object, not a class; typos resolving to undefined under a permissive tsconfig; trying to wrap a plain factory function that is not a constructor.","solutions":["Pass the class itself, not an instance: `new ClassType(Point, ...)` instead of `new ClassType(new Point(), ...)`.","Check `typeof value === 'function'` and that it has a prototype before wrapping.","If the value is a plain factory function, convert it to a class or wrap instances of its results individually with ClassInstance.","Verify imports resolve to the class (check for wrong default/named import or undefined under ESM circular imports)."],"exampleFix":"// before\nconst point = new Point(1, 2)\nnew ClassType(point, { init: true }) // TypeError\n// after\nnew ClassType(Point, { init: true, instanceEagerAttrs: 'all' })","handlingStrategy":"validation","validationCode":"function isClass(value: unknown): value is new (...args: never[]) => object {\n  return typeof value === 'function' && /^class\\s/.test(Function.prototype.toString.call(value))\n}\nif (!isClass(maybeClass)) throw new TypeError('ClassType requires a class constructor')","typeGuard":"function isConstructorFunction(value: unknown): value is new (...args: never[]) => object {\n  if (typeof value !== 'function') return false\n  const desc = Object.getOwnPropertyDescriptor(value, 'prototype')\n  return desc !== undefined && !desc.writable // class constructors have non-writable prototype\n}","tryCatchPattern":"try {\n  const type = new ClassType(value, { init: true })\n} catch (e) {\n  if (e instanceof TypeError && /expects a class/.test(e.message)) {\n    throw new TypeError(`${debugName(value)} is not a class — pass the constructor, not an instance`)\n  } else throw e\n}","preventionTips":["Type parameters as `new (...args: never[]) => object` so TS rejects non-constructor values at compile time","Remember Point vs new Point — always wrap the class object","Verify imports: a wrong default import may yield undefined or an object at runtime","Plain factory functions are not classes; convert them or wrap their products with ClassInstance instead"],"tags":["typescript","type-mismatch","constructor","wrapper"],"backgroundTag":"invalid-constructor-argument","analyzedSha":"adc986b362e3961f407868cb118a99fe831b9e61","analyzedAt":"2026-09-13T19:19:18.698Z","contentChangedAt":"2026-09-13T19:19:18.698Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}