fabian-hiller / valibot

The modular and type safe schema library for validating structural data 🤖
https://valibot.dev
MIT License
6.32k stars 204 forks source link

TypeScript Type Issue with Optional Fields in Valibot Schema Parsing #880

Closed AndreiSoroka closed 1 month ago

AndreiSoroka commented 1 month ago

Problem Description:

When using the valibot library with TypeScript, there’s a discrepancy in how optional fields are typed. Specifically, the optional validator is not retaining the undefined type for the password field. The expected type for the parsed result should be {email: string, password: string | undefined}, but the actual result is typed as {email: string, password: string}, suggesting that password is always present.

import * as v from 'valibot';

const Schema = v.object({
  email: v.pipe(v.string(), v.email()),
  password: v.optional(v.pipe(v.string(), v.minLength(8))),
});

const result = v.parse(Schema, {
  email: 'jane@example.com',
  // password: '12345678',
});

const test = {
  email: result.email,
  password: result.password
};
// expected type {email: string, password: string | undefined}
// current result: {email: string, password: string}

console.log(test);

Expected Behavior:

The parsed result should have a type of {email: string, password: string | undefined} to properly account for the absence of the password field in the input data.

Actual Behavior:

The result is typed as {email: string, password: string}, which implies that password is always defined, even if it is actually optional and can be absent.

Possible Cause:

This issue might be due to how valibot processes optional fields, or it could be a limitation in how TypeScript infers the types from the valibot schema. Even when password is omitted, it is still being treated as a string instead of string | undefined.

Examples:

In the playground correct behaviour: playground

image

With typescript v5.6.3:

image

and

image

My ts config:

{
  "compilerOptions": {
    "target": "ES2020",
    "moduleDetection": "force",
    "module": "Preserve",
    "resolveJsonModule": true,
    "allowJs": true,
    "esModuleInterop": true,
    "isolatedModules": true,
    "allowImportingTsExtensions": true
  }
}
AndreiSoroka commented 1 month ago

Sorry, fixed

tsconfig: "strictNullChecks": true