Skip to content

Bug with InferOutput and nullish, optional, exactOptional, nullable #1376

@Bilboramix

Description

@Bilboramix

Hello there, we found a bug in the discord channel in this schema :

import * as v from 'valibot';

const Schema = v.object({
	data: v.pipe(
		v.nullish(v.object({
			videos: v.array(v.string()),
    	})),
		v.transform(val => val?.videos ?? [])
	),
})

type Foo = v.InferOutput<typeof Schema>;

// Foo['data'] should be only string[], not string[]|undefined

When we are using object with an array child AND if the object is using nullish | optional | exactOptional | nullable WITH a default value then InferOutput return the same type as InferInput instead of using the default value to evaluate output type.

So to have a better view about what's going on i tested it as well :

import * as v from 'valibot';

// This one does not work as expected
const nullish0 = v.nullish(v.object({ foo: v.array(v.string()) }), {
  foo: [],
});
type nullishI0 = v.InferInput<typeof nullish0>;
type nullishO0 = v.InferOutput<typeof nullish0>;

// So we would have to do such tricks to make it works :
const nullish1 = v.pipe(
  v.nullish(v.object({ foo: v.array(v.string()) })),
  v.transform((input) => {
    if (!input) {
      return { foo: [] };
    }

    if (!input.foo) {
      input.foo = [];
      return input;
    }
    return input;
  }),
);
type nullishI1 = v.InferInput<typeof nullish1>;
type nullishO1 = v.InferOutput<typeof nullish1>;

// However we do not have the same problem if `array` is not called inside an `object` call :
const nullish2 = v.nullish(v.object({ foo: v.string() }), {
  foo: '',
});

type nullishI2 = v.InferInput<typeof nullish2>;
type nullishO2 = v.InferOutput<typeof nullish2>;

const nullish3 = v.nullish(v.array(v.string()), []);

type nullishI3 = v.InferInput<typeof nullish3>;
type nullishO3 = v.InferOutput<typeof nullish3>;

const nullish4 = v.nullish(v.array(v.object({ foo: v.string() })), []);

type nullishI4 = v.InferInput<typeof nullish4>;
type nullishO4 = v.InferOutput<typeof nullish4>;

This schema can be easily edited to try with other actions with a CTRL + D edit and copy paste.

To see the problem with exactOptional we need a parent object :

import * as v from 'valibot';

const exactOptional0 = v.object({
  data: v.exactOptional(v.object({ foo: v.array(v.string()) }), {
    foo: [],
  }),
});
type exactOptionalI0 = v.InferInput<typeof exactOptional0>;
type exactOptionalO0 = v.InferOutput<typeof exactOptional0>;

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingpriorityThis has priority

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions