Home > Software design >  infer type from constructor options
infer type from constructor options

Time:02-06

This works, but I'm sure there's a nicer way of doing this, I'm using zod for schema validation on values i have zero control over from the user.

const SchemaValue = z.object({
  translatedValue: z.string().or(z.number())
});

const SchemaInput = z.object({
  truncate: z.number()
});

I then have a very simple class, which takes those schemas and performs validation, and also infers the type back to a method passed to the "Action".

interface ActionOptions<I, V> {
  name: string;
  inputSchema: z.AnyZodObject;
  valueSchema: z.AnyZodObject;
  validator: (input: I, value: V) => Promise<[string | null, boolean]>;
}

export class Action<I, V> {
  options: ActionOptions<I, V>;
  constructor(options: ActionOptions<I, V>) {
    this.options = options;
  }

  async run(value: V, input: I): Promise<void> {
    // additional code is here to validate value & input against valueSchema and inputSchema
    await this.options.validator(
      input,
      value,
    );
  }
}

I then use this like so:

const action = new Action<z.infer<typeof InputSchema>, z.infer<typeof ValueSchema>>({
  inputSchema: InputSchema,
  valueSchema: ValueSchema,
  name: 'someAction',
  async validator(input, value) {
    return [null, true];
  }
});

This works, intellisense on validator for input & value is correct, however I just can't help but think there's a nicer way of achieving this without having to pass through the types to the constructor, the only reason I'm asking is I'm trying to make the use of Action as simple as possible!

I'm wondering if there's a way to infer the types from inputSchema and valueSchema without passing them through the constructor when calling a new instance?

CodePudding user response:

The problem

z.AnyZodObject is not a generic and erases any automatic inference.

The solution

Use z.Schema<T> to automatically infer the type since it is a generic.

Example

...
interface ActionOptions<I, V> {
  name: string;
  inputSchema: z.Schema<I>;
  valueSchema: z.Schema<V>;
  validator: (input: I, value: V) => Promise<[string | null, boolean]>;
}
...

const action = new Action({
  inputSchema: InputSchema,
  valueSchema: ValueSchema,
  name: 'someAction',
  async validator(input, value) {
    return [null, true];
  }
});
  • Related