Giter Club home page Giter Club logo

valibot's People

Contributors

alonidiom avatar andreyyolkin avatar ariskemper avatar bastidood avatar chimame avatar danielwinkelmann avatar ecyrbe avatar fabian-hiller avatar floriandevphynix avatar genki avatar gmaxlev avatar hugos68 avatar kazizi55 avatar kurtextrem avatar mini-ghost avatar morinokami avatar mrsekut avatar mwskwong avatar qlaffont avatar romeerez avatar roottool avatar saeris avatar samualtnorman avatar sifferhans avatar sillvva avatar sqmasep avatar victornswd avatar wout-junius avatar xcfox avatar zkulbeda avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

valibot's Issues

feature request: i18n

Thanks for your great library.
I think multilingual support is important for this library.
For example, our company chose dayjs instead of date-fns because i18n is far more easy to apply Japanese.
Would you please consider support i18n?

Add an option i18n to parse function:

parse(input: unknown, info?: ParseInfo, i18n?: Record<string, string>)

And get message from i18n in each function:

throw new ValiError([
  {
    validation: 'ends_with',
    origin: 'value',
    message: error || i18n?.["end_with"] || 'Invalid end',
    input,
    ...info,
  },
]);

So user can only define the i18n for the functions they're using to reduce bundle size. I'm not sure this implementation is OK for this library.

docs: Some more use cases

Hey,

There are also more use cases for valibot. One would be to validate the data that is stored in the Localstorage. And another use case would be to validate the schema of your URL search parameters.

Conditionally requiring object fields

Hi! Thank you very much for this library. I do very appreciate the focus on performance and very high composability of rules! It's super awesome! Also congratulations of a first release!
I came here with this issue, because to this day I'm using Zod, since it's doing fine for me so far, but my biggest nitpick there is that there is no easy way to declare conditional requiring of the object properties. I wonder if there is a possibility to do that here

What do I mean specifically?

I mean that you can mark an object property as required (I'm assuming that's by default here), but only when given condition is met

How it could look like

f.e.

import { parse, object, string, requiredWhen } from 'valibot';

function getFormValidationSchema(dataToValidate) {
  const formSchema = object({
    character: {
      type: string(),
    },
    shurikensAmount: number([
      requiredWhen(() => dataToValidate.character.type === 'ninja'),
      minRange(1), 
      maxRange(3)
    ]),
  });
  return formSchema;
}

const formValidationSchema = getFormValidationSchema(data);
parse(formValidationSchema)

With data like this

type CharacterType = 'ninja' | 'warrior' | 'wizard';

const data = {
  character: {
     type: 'ninja' as CharacterType
  },
  shurikensAmount: null
};

parse() should throw an error, because shurikensAmount is null and it's required for character.type === 'ninja'
but with data like this

type CharacterType = 'ninja' | 'warrior' | 'wizard';

const data = {
  character: {
     type: 'wizard' as CharacterType
  },
  shurikensAmount: null
};

should be fine, since wizards are not using shurikens

Conclusion

I'm not sure if this is a good idea or not. I'm only giving an idea for a solution for my problem where I struggled with trying to achieve this in Zod. I had to use refine() in this case, but it was more of a workaround rather than proper solution.
link: colinhacks/zod#938 (comment)
Since this library focus on high composability of rules, I wonder if it there is a possibility to do that here.
If this is not a good idea, I will appreciate proper explanation
Have a good day!

useDefault is treated as a React hook unintentionally

When using Valibot in a React project, as demonstrated by this StackBlitz example,
Just using useDefault at top-level like const schema = useDefault(string(), 'test') causes the following ESLint error:

 3:16  error  React Hook "useDefault" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function  react-hooks/rules-of-hooks

This error is caused by eslint-plugin-react-hooks, which is maintained by the React team and recommended officially in the doc.
Basically every well maintained React projects should have installed this ESLint rule.

Semantically, useSomeIdentifierHere is considered as a React hook by its naming convention and tools like the ESLint rule is following this definition. Note that this is not only about ESLint, other tools such as Fast Refresh implementation is also relying on this naming convention. People might be confused since useDefault looks like a React hook as well.

Usually it's weird to be constrained by "just a single framework" such as React but React is used extremely a lot and it would be nice if Valibot can be a good companion of React.

I searched Valibot repo and did the following Ripgrep search:

$ valibot/library rg  'use[A-Z]'
src/methods/index.ts
14:export * from './useDefault/index.ts';

src/methods/useDefault/index.ts
1:export * from './useDefault.ts';

src/methods/useDefault/useDefault.test.ts
4:import { useDefault } from './useDefault.ts';
6:describe('useDefault', () => {
8:    const schema1 = useDefault(string(), 'test');

src/methods/useDefault/useDefault.ts
11:export function useDefault<TSchema extends BaseSchema | BaseSchemaAsync>(

So we only need to think about an alternative name of useDefault.
My suggestion so far is to rename it to something like withDefault but there might be a better name

feature: add `formData` schema

Hi,
I don't know how this would affect bundle size, but I think it would be interesting to have an API like :

const reportSchema = formData({
  title: string(),
  content: string(),
  photo: file(),
});

It could be useful in server actions (Next.js) and client-side validations in <form>.

Type instantiation is excessively deep and possibly infinite.

Hello and first of all, thank you for your work with this library.
I was eager to migrate from zod to valibot after encountering this error on the use of schemas with many union and transform but the same one seems to occur.
Is there maybe a good practice to avoid it?
Thank you

Improved inferred type for Records

Take this object, created with zod:

const ZodProblemDetailsSchema = z.object({
  errors: z.record(z.array(z.string())).optional(),
});

When we infer the type of this schema with the z.infer method, we see that the type inferred for the Record is quite simple:

type ZodProblemDetails = {
    errors?: Record<string, string[]> | undefined;
}

I've implemented the same thing with valibot:

const ProblemDetailsSchema = object({
  errors: optional(record(array(string()))),
});

The type inferred with Output gives this:

type ProblemDetails = {
    errors: RecordOutput<StringSchema, ArraySchema<StringSchema<string>, string[]>> | undefined;
}

Would it be possible to come up with a type similar to zod Records for valibot Records? They seem more readable / simple to understand!

Thank you!

feature: validate extended ISO datetimes

Trying to validate the datetime "2023-08-05 14:41:39.589Z" with isoDateTIme validator, I got an error so, inspecting the implementation I found there is no case to considering an ommited separator and ISO 8601-1:2019 allows the T to be omitted in the extended format.

Maybe it could be the case of creating a extendedIsoDateTime validator in validations folder?

How to make a discriminatedUnion like in zod?

How can I achieve a similar functionality to Zod's discriminatedUnion API in Valibot? I want to enable dynamic properties in an object.

Example of zod's discriminatedUnion api:

const response = z.discriminatedUnion("status", [
  z.object({
    status: z.literal("success"),
    data: z.string(),
  }),
  z.object({
    status: z.literal("failed"),
    error: z.instanceof(Error),
  }),
]);

myUnion.parse({
  status: "success",
  data: "yippie ki yay",
});

Ref: discriminatedUnion in zod

Catch/Fallback

zod's default() only works with undefined and not null, which this library perfectly mimics. Zod Sandbox Example

Thanks to catch() on the other hand, it is possible to have a default value when validation fails. But Valibot seems to not have catch().
It is one of the most useful functions of zod (atleast to me) and I use it a lot. Especially with react, where I want the ensure the app always has certain pieces of data.

Thanks to the simplicity of Valibot is was able to quickly write a catch method.

import type { BaseSchema, Output } from 'valibot';
import { ValiError } from 'valibot';

function Catch<TSchema extends BaseSchema>(
  schema: TSchema,
  value: Output<TSchema>,
  callback?: (err: Error) => void
): TSchema {
  return {
    ...schema,
    parse(input, info) {
      try {
        return schema.parse(input, info);
      } catch (err: any) {
        if (typeof callback === 'undefined') {
          console.error(err);
        } else {
          callback(err);
        }
        return value;
      }
    },
  };
}

Sandbox with usage examples
npm run fallback

This method could also be called fallback. I originally called it that, but to stay true to zod's naming I changed it to Catch, just not catch (reserved word).

Small bundle size starting at less than 300 bytes

First of all I recommend turning on discussions since technically speaking this is not an issue.

I'd like to ask how those 300 bytes in README in a sentence "Small bundle size starting at less than 300 bytes" were determined.

Did you import only let's say string? Or number? Or something else?

Thank you.

Minify dist files with tsup

I have forked your project to have a look and I noticed a small thing. The tsup config file has no minify property. Was it intended to not put it in there?

Not sure how it will effect bundle size in general and I would like to learn more.

I've just in case put a comparison between disabling and enabling.

When minify is set to false the javascript bundle size exceeds to:

  • cjs: 98.04kb
  • esm: 92.87kb

When minify is set to true the javascript bundle size exceeds to:

  • cjs: 31.94kb
  • esm: 31.36kb

I can make a PR if you wish to implement or you see don't have to.

[Custom] How to match valibot custom to zod custom in this case bellow

Hi @fabian-hiller, your libs are amazing! But can you share with me how to custom property in an object like Zod does?

I have a generic typing function like this

export const baseQuerySchema = <T>() =>
  object({
    order: custom<`${string & keyof T}:${"asc" | "desc"}`>((val) =>
      /\w+:(asc|desc)/.test(val as string),
    ),
    pageSize: optional(transform(useDefault(string(), "20"), (v) => parseInt(v, 10))),
    pageIndex: optional(transform(useDefault(string(), "0"), (v) => parseInt(v, 10))),
  })

How can I transform order filed to valibot ๐Ÿ˜† The typescript compiler complains

No overload matches this call.
  Overload 1 of 2, '(object: ObjectShape, pipe?: Pipe<{ [x: string]: any; }>): ObjectSchema<ObjectShape, { [x: string]: any; }>', gave the following error.
    Type '(input: `${string & keyof T}:asc` | `${string & keyof T}:desc`, info: ValidateInfo) => `${string & keyof T}:asc` | `${string & keyof T}:desc`' is not assignable to type 'BaseSchema<any, any>'.
  Overload 2 of 2, '(object: ObjectShape, error?: string, pipe?: Pipe<{ [x: string]: any; }>): ObjectSchema<ObjectShape, { [x: string]: any; }>', gave the following error.
    Type '(input: `${string & keyof T}:asc` | `${string & keyof T}:desc`, info: ValidateInfo) => `${string & keyof T}:asc` | `${string & keyof T}:desc`' is not assignable to type 'BaseSchema<any, any>'.ts(2769)

Thanks for reading this!

Merge a transformed schema

Hello,
In zod, I was used to bump my head around trying too many times to extend a transformed schema.
With valibot, I refactored my code with merge and did not encounter any errors BUT the merged schema (that uses transform) was the input instead of the output of the transformation. Is it intended?
Otherwise, what is the correct way to do it?
Thank you

Tracking issue: Performance improvement

This is a tracking issue to discuss & improve the performance of Valibot.
I created a benchmark suite for Valibot so that we can track its runtime performance in many situations since existing benchmarks were failing to capture the real world schema and data diversity.

The current latest result can be checked from https://naruaway.github.io/valibot-benchmarks/

Currently we see the following interesting observations:

  • Most of the cases, Valibot is slower than Zod
  • Valibot performs better in Bun (JavaScriptCore)
  • When the data is invalid, Valibot is much slower than Zod, which is probably because we rely on exception handling for failure case

I'll continue improving my benchmark suite and will post any interesting information here.

We already have several performance improvement PRs: https://github.com/fabian-hiller/valibot/pulls?q=is%3Apr+is%3Aopen+label%3Aperformance

I'll post benchmark result for each PR later

Recursive schema

Hello there!
At the moment there seems to be no way to use a recursive schema.

Zod for example does it like this:

const baseCategorySchema = z.object({
  name: z.string(),
});

type Category = z.infer<typeof baseCategorySchema> & {
  subcategories: Category[];
};

const categorySchema: z.ZodType<Category> = baseCategorySchema.extend({
  subcategories: z.lazy(() => categorySchema.array()),
});

Maybe there even is a way to do this without having to manually define a type, I know some validation lib had that feature, I just can't remember which one it was. It might not be possible with the way Valibot works, though.

Any plan to improve performance?

I write a simple benchmark with real case.

  • zod
  • yup
  • joi
  • valibot
import Benchmark from "benchmark";

import { z } from "zod";
import * as yup from "yup";
import Joi from "joi";
import * as valibot from "valibot";

const SUITE_NAME = "";
const suite = new Benchmark.Suite("Validate Benchmark");

const value = {
  latitude: 10,
  longitude: 10,
  scale: 10,
  name: "",
  address: "",
  powerLevel: "low",
};

const stringSchema = z.object({
  latitude: z.number(),
  longitude: z.number(),
  scale: z.number().min(5).max(18),
  name: z.string().optional(),
  address: z.string().optional(),
  powerLevel: z.enum(["low", "medium", "high"]),
});

const yupSchema = yup.object({
  latitude: yup.number(),
  longitude: yup.number(),
  scale: yup.number().min(5).max(18),
  name: yup.string().optional(),
  address: yup.string().optional(),
  powerLevel: yup.mixed().oneOf(["low", "medium", "high"]),
});

const joiSchema = Joi.object({
  latitude: Joi.number(),
  longitude: Joi.number(),
  scale: Joi.number().min(5).max(18),
  name: Joi.string().optional(),
  address: Joi.string().optional(),
  powerLevel: Joi.string().valid("low", "medium", "high"),
});

const valibotSchema = valibot.object({
  latitude: valibot.number(),
  longitude: valibot.number(),
  scale: valibot.number([valibot.minRange(5), valibot.maxRange(18)]),
  name: valibot.optional(valibot.string()),
  address: valibot.optional(valibot.string()),
  powerLevel: valibot.enumType(["low", "medium", "high"]),
});

suite
  .add("zod validate", () => {
    stringSchema.safeParse(value);
  })
  .add("yup validate", () => {
    yupSchema.validate(value);
  })
  .add("joi validate", () => {
    joiSchema.validate(value);
  })
  .add("valibot validate", () => {
    valibot.safeParse(valibotSchema, value);
  })
  .on("cycle", (e: Benchmark.Event) => {
    console.log(`${SUITE_NAME}: ${e.target}`);
  })
  .on("complete", function () {
    console.log("Fastest is " + this.filter("fastest").map("name"));
    process.exit(0);
  })
  .run();
image

Zod is so fast, is my case not good or any plan to improve performance?

Multiple brands schema results to never

Hi! Current implementation of branded types uses single symbol for single branded type, that results to never type in tests

test('should allow multiple branding', () => {
const branded1 = brand(string(), '1');
const branded2 = brand(branded1, '2');
type Branded2 = Output<typeof branded2>;
expectTypeOf(branded1).not.toEqualTypeOf(branded2);
expectTypeOf<Branded2>().toEqualTypeOf<string & Brand<'1'> & Brand<'2'>>();
});

image

It happends because typescript resolves { [symbol]: "1"} & { [symbol]: "2" } to never.

I'll make a pr for fix, that replaces single value to object of values

discord?

any plans to make a discord server? i would put that question in discussion but it not available :D

Integrate with Superforms, and toward a validation standard

Hello Fabian and the rest of the team, I'm Andreas, the author of the SvelteKit Superforms library. Nice project you've got going! You talked about integrations in your introductory post, it would be very nice to be able to use valibot in Superforms, as I'm planning to support multiple validation libraries in v2.0.

It's not as simple as providing schema validation though, for a complete integration there need to be some schema introspection available. I've started a discussion at typeschema about the requirements: decs/typeschema#9

It would be interesting to hear your thoughts about this. I'm frankly a bit worried that such a basic problem as form validation has become so fragmented, with a dozen libraries doing the same thing differently, instead of focus being on standards.

feature: add branded types

Introduce brand method

const BrandType: unique symbol = Symbol()

interface Brand<K extends string | symbol> {
  readonly [BrandType]: {
    readonly [k in K]: K
  }
}

type Branded<T, K extends string | symbol> = T & Brand<K>

function brand<TSchema extends BaseSchema, const K>(
  schema: TSchema,
  brand: K
): BaseSchema<Input<TSchema>, Branded<Output<TSchema>, K>>{
  return schema
}

const schema = brand(number([maxValue(20)]), "UserId");
parse(schema, 123) // number & Brand<"UserId">

Validate schema against existing type

Hi, first of all, thanks for Valibot, looks really cool ๐Ÿ™Œ

One feature that I was looking for was Yup's Ensuring a schema matches an existing type, which could look like this in Valibot:

import { type ObjectSchema, email, minLength, object, string } from 'valibot';

type LoginInput = {
  email: string;
  password: string;
};

// Check that the schema conforms to the `LoginInput` type
const LoginSchema: ObjectSchema<LoginInput> = object({
  email: string([email()]),
  password: string([minLength(8)]),
});

What do you think of this feature?

Links on valibot.dev does not work

Hello

I tried reading the documentation on valibot.dev, but nothing happens when I click on the "Guides" and "API Reference" links.

Browser

Google Chrome | 115.0.5790.114ย (Official Build)ย (arm64)
-- | --
Revision | 67277e15806bf7f0307b37cd6f8f29e71b615796-refs/branch-heads/5790_90@{#25}
OS | macOS Version 13.5 (Build 22G74)
JavaScript | V8ย 11.5.150.16
User agent | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36

Demo video

valibot-dev-issue.mp4

Discussion about `passthrough` to parse object keeping extra props

Add passthrough method inspired by Zod to allow parse object() keeping extra props that are present on original input object but not validated through Schema.

This maybe useful if you want to validate/trasform only "a portion of an object" that you receive from/send to and API, but want to keep all the original extra props present in the object that are not specified in object() schema.

PS: some more discussion points:

  1. if you prefer other name please let me know, I just used the same name of Zod... but I'm open to alternative/refactor
  2. do you think that it's correct to implement it as a "wrapper" and my modification to unwrap or do you prefer other way to implement it ?
  3. about this I was even thinking to implement it changing the object() implementation adding another overload with an optional strict: boolean param before error - but to do it I need to change the implementation of getErrorAndPipe handling 3 params to discriminate boolean, string, Pipe[] ... I don't know if you'll approve/prefer this kind of implementation, maybe the result is more clear because the real nature of passthrough is just an ObjectSchema that keep the extra params, but don't know if you like it...
  4. Or maybe on the same "idea" i can create other 2 function objectWithExtraProps that return ObjectSchema but simply use my parse code to keep props...

This are all the ideas I came up when implementing it, what's your opinion?

Originally posted by @dmorosinotto in #15 (comment)

Validating number ranges

I couldn't find anyway to validate that a number is above/below some value or inside a range. Neither for inclusive ranges or exclusive ones.

In Zod this is done with the gt and lt for exclusive ranges and gte and lte for inclusive ranges. (Zod also has positive, negative, nonpositive and nonnegative for ranges bounded by 0).
https://zod.dev/?id=numbers

(It is possible that this feature is already implemented in valibot and I just couldn't find it)

feature: add transform and validate method

Let's imagine I expect a string "10 ms" on input and want to validate both string and number parts of it, and expect output as number 10
Currently it could be written something like this

transform(string([endsWith(" ms")]), (value) =>
  parse(coerce(number([maxValue(20)]), Number), value.split(" ")[0]),
);

Add a third optional argument to transform func as transform output schema or introduce transformAndValidate method

transform(
  string([endsWith(" ms")]), 
  (value) => value.split(" ")[0], 
  coerce(number([maxValue(20)]), Number)
);

Capitalized variables

Not that important, but why do the docs capitalize the names of schema variables?

E.g. why EmailSchema, not emailSchema?

Valibot to JSON Schema

Will there a native valibot โ†” json schema conversion?

I think this is what missing from zod (you have to use zod-to-json-schema, which is a third party package with some known issues)

Whereas in the python ecosystem, pydantic has native support to generate json schemas from models.

Lovely work so far!

any chance to support date-io lib integrations?

Working with dates in javascript is horrible since the support is quite bad, there are libraries that know how to manage dates quite well.

There is a project that integrates a few libraries called date-io, would it be possible to add support to be able to handle the dates of the integrations here instead of working with Date from javascript?

The libraries would be the ones listed here: https://github.com/dmtrKovalenko/date-io/tree/master#projects

I use js-joda a lot to do operations on dates such as add N day/month/year, date before/after? and the like (to name a few).

Consider "ctx.addIssue" style rather than throwing ValiError

Throwing errors is really expensive operation so even if we can make parse fast for "success" case, "failure" case will be still slow. This means if we use Valibot for server side validation logic, malicious people might be able to attempt DoS by sending "failure" payload.

I created this issue to discuss architectural / philosophical possibility to stop throwing ValiError without compromising other factors such as bundle size and code simplicity.

How validate object with hyphen in the keys

Hi
May I know how to create a schema to validate an object with hyphen in the key, e.g. X-API-Key

const ApiKeySchema = object({
  headers: object({
    "X-API-Key": string("API key is missing", [minLength(1)]),
  }),
});

Improving the inferred type of an optional property

Take this object, created with zod:

const ZodProblemDetailsSchema = z.object({
  detail: z.string().optional(),
  status: z.number().optional(),
  title: z.string().optional(),
  type: z.string().optional(),
});

When we infer the type of this schema with the z.infer method, we see that all properties are optional

type ZodProblemDetails = {
    detail?: string | undefined;
    status?: number | undefined;
    title?: string | undefined;
    type?: string | undefined;
}

I've implemented the same thing with valibot:

const ProblemDetailsSchema = object({
  detail: optional(string()),
  status: optional(number()),
  title: optional(string()),
  type: optional(string()),
});

The type inferred with Output gives this:

type ProblemDetails = {
    detail: string | undefined;
    status: number | undefined;
    title: string | undefined;
    type: string | undefined;
}

Would it be possible, as zod does, to mark optional properties of an object as optional, and not just possibly undefined?

(Nice work on this library, by the way!)

Allow schema serialization

Schema serialization will make this package a good alternative for JSON schema.

Example:

import { serialize, deserialize, parse } from 'valibot';

const serializedLoginSchema = serialize(LoginSchema);
const deserializedLoginSchema = deserialize(serializedLoginSchema);

const parsedData = parse(deserializedLoginSchema, { email: '[email protected]', password: '12345678' })

console.log(parsedData); // { email: '[email protected]', password: '12345678' }

Serialization won't be possible if transform or any other method that has JS callbacks.

[Bug] Merge/Transform make all properties in object to optional field

Hi @fabian-hiller, I have an object like this

    object({
      description: optional(string()),
      isActive: optional(useDefault(boolean(), true)),
      link: string(),
      title: string(),
      type: nativeEnum(MenuType),
    })

But when I wrap this object with transform or merge --> All properties mark optional, might be this is an expected behavior or a bug ๐Ÿค”. Hope you can check this!

Publish to deno.land/x

Hello there! Awesome project here. I'd really love to try this library out on my Deno projects. Although I may already do so by directly importing the TypeScript files from GitHub itself, it would be great to have them officially published at deno.land/x for better discoverability.

Enum type returns string

I have a schema like this:

const postSchema = object({
    status: optional(enumType(['publish', "future", "draft", "pending", "private", "trash", "auto-draft", "inherit"]))
});

However, this returns a string type. While it does validate this correctly, the typing is not the correct type.

This is not the expected result as 'string' and 'enum' are completely different. If I pass the result.data.status to an enum type, I get an error because it is a string type, while technically validated.

J

Benchmarks and comparisons

It'd be really nice to see comparative benchmarks with other similar libs and how they compare with valibot.

Feedback: function names

For my taste the function names are too close to the type names in typescript. I guess zod evaded that by picking a namespace. This choice could really be a bad part of dev experience.

Add built in type guards

Usually when using zod a common pattern I see is defining a type guard around the safeParse that would look something like:

type Data = z.infer<typeof dataSchema>
const isData = (input: unknown): input is Data => {
  return dataSchema.safeParse(input).success
}

So I would suggest adding something like an is function directly in the library to fill this function so that users don't have to create these wrapper functions.

For an example let's say you have a React component that listens to messages and if it's a specific type of message then it sets some local state. Without a built in type guard it would look something like this:

const messageDataSchema = object({
  eventId: literal('exampleEventId'),
  id: string(),
})

type MessageData = Output<typeof messageDataSchema>

const isMessageData = (input: unknown): input is MessageData => {
  return safeParse(messageDataSchema, input).success
}

const Component = () => {
  const [id, setId] = useState('')
  useEffect(() => {
    const handler = ({ data }: MessageEvent['data']) => {
      if (isMessageData(data)) {
        setId(data.id)
      }
    }
    window.addEventListener('message', handler)
    return () => {
      window.removeEventListener('message', handler)
    }
  }, [])
}

So we create the schema, infer that to a type, use the type to create a type guard, and then we can use that type guard in our message handler. Alternatively if we have built in type guards we can do:

const messageDataSchema = object({
  eventId: literal('exampleEventId'),
  id: string(),
})

const Component = () => {
  const [id, setId] = useState('')
  useEffect(() => {
    const handler = ({ data }: MessageEvent['data']) => {
      if (is(messageDataSchema, data)) {
        setId(data.id)
      }
    }
    window.addEventListener('message', handler)
    return () => {
      window.removeEventListener('message', handler)
    }
  }, [])
}

Now we create the schema and use that directly with the type guard which results in less code for the user to write. Of course we could use safeParse directly and use its return value and be type safe like this:

const messageDataSchema = object({
  eventId: literal('exampleEventId'),
  id: string(),
})

const Component = () => {
  const [id, setId] = useState('')
  useEffect(() => {
    const handler = ({ data }: MessageEvent['data']) => {
      const res = safeParse(messageDataSchema, data)
      if (res.success) {
        setId(res.data.id)
      }
    }
    window.addEventListener('message', handler)
    return () => {
      window.removeEventListener('message', handler)
    }
  }, [])
}

Personally I would prefer following the more standard format of type guards being their own functions as in the first two examples. With the third example, while working without issues, we split our checking over multiple lines and we don't narrow the type of our argument, instead we create a new variable to hold the typed data which I would also argue is not preferred.

[Input]: Not extract the TypeScript type of nested schema

Hi and thank you for your time on it
With the simple code below or Playground
Why do types in name still contain v.ObjectInput?

import * as v from "valibot"; // 0.74 kB

const loginSchema = v.object({
  email: v.string([
    v.minLength(1, "Please enter your email."),
    v.email("The email address is badly formatted."),
  ]),
  password: v.string([
    v.minLength(1, "Please enter your password."),
    v.minLength(8, "You password must have 8 characters or more."),
  ]),
  name: v.object({
    first: v.string([v.minLength(1, "Please enter your first name.")]),
    last: v.string([v.minLength(1, "Please enter your last name.")]),
  }),
});

type Login = v.Input<typeof loginSchema>;
//   ^?

image

I think, all examples on documentation should use a wildcard import to easy for the reader and less confusing

check minLength of an array

hi i have this

export const pollOptions = object({
  //WIP we need more
  answer: string([minLength(3)]),
  id: optional(union([string(), number()])),
  order: number()
});
...
pollOptions: array(pollOptions), 
...

is there are a way to check pollOptions is min 1 (2 entries) long or do i have to use custom?

thanks

emoji in email address

In the recent years it is possible to register an email address with emoji in domain name and main address. Does this library support such interesting input? Or was some specification used as reference to validate the email?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.