valibot icon indicating copy to clipboard operation
valibot copied to clipboard

Support classes with private constructors in v.instance

Open eonicum opened this issue 7 months ago • 2 comments

Use case

Encapsulating a class constructor and exposing a static factory method for instantiation


import * as v from 'valibot';

class Some {
  private constructor(readonly value: number) {
  }

  static fromValue(value: number) {
    return new Some(value);
  }
}

// TS2345: Argument of type typeof Some is not assignable to parameter of type Class
// Cannot assign a private constructor type to a public constructor type.
const SomeSchema = v.instance(Some);

Issue

The error occurs because Valibot's Class type expects a class with a public constructor, but Some has a private constructor, making it incompatible.

Potential solution

To resolve this, Valibot's Class type can be redefined to be more flexible, allowing classes with private constructors to work with v.instance.

// Current
export type Class = new (...args: any[]) => any;

// Improved
export type Class<T> = {
  new (...args: any[]): T;
};

eonicum avatar Apr 20 '25 08:04 eonicum

the proposed solution does not solve the issue, the typescript error is still raised.

related: https://github.com/colinhacks/zod/issues/1065

I don't think there's a way to both check that a value is constructable while allowing private classes, unfortunately. NewableFunction allows assignment from private constructors, but it's then impossible to retrieve InstanceType<TClass>

As with the linked issue, you'd likely have to rely on v.custom<Some>((arg) => arg instanceof Some).

EskiMojo14 avatar Apr 20 '25 11:04 EskiMojo14

Unfortunately, the TypeScript type NewableFunction is not compatible with InstanceType. The only thing we could consider is adding // @ts-expect-error if there is no downside. I also recommend using custom in the meantime.

fabian-hiller avatar Apr 20 '25 20:04 fabian-hiller

Hi, @eonicum. I'm Dosu, and I'm helping the Valibot team manage their backlog. I'm marking this issue as stale.

Issue Summary:

  • The issue involves Valibot's v.instance function not supporting classes with private constructors.
  • The current Class type definition is causing a TypeScript error.
  • @EskiMojo14 and @fabian-hiller have acknowledged the problem and discussed potential workarounds.
  • Suggested workarounds include using v.custom<Some>((arg) => arg instanceof Some) or // @ts-expect-error.

Next Steps:

  • Please confirm if this issue is still relevant to the latest version of the Valibot repository by commenting on this issue.
  • If no further activity occurs, this issue will be automatically closed in 30 days.

Thank you for your understanding and contribution!

dosubot[bot] avatar Jul 20 '25 16:07 dosubot[bot]