Giter VIP home page Giter VIP logo

Comments (18)

fabian-hiller avatar fabian-hiller commented on September 21, 2024 2

The next version of Valibot will allow you to write the following code to solve your problem. Feel free to give me feedback on this in the long run.

import * as v from 'valibot';

const ListSchema = v.picklist(['external', 'online', 'standard', 'telephone']);

const ObjectSchema = v.object(v.entriesFromList(ListSchema.options, v.number()));

from valibot.

macmillen avatar macmillen commented on September 21, 2024 1

Thank you, I'll do that :)

from valibot.

ksv90 avatar ksv90 commented on September 21, 2024 1

Thank you very much for your promptness. Now this also works with numbers and symbols.
I'll try to study the issue. If I have a proposal, I will publish it.

from valibot.

fabian-hiller avatar fabian-hiller commented on September 21, 2024

This is the intended behavior when string is not used directly as the key schema. In your case, I recommend removing union and using only string. This should solve your problem. union can be used with literal to describe certain record keys, but record does not force you to specify them and therefore we mark it as optional in this case. Feel free to contact me if you have any further questions.

from valibot.

fabian-hiller avatar fabian-hiller commented on September 21, 2024

But please leave this issue open. I agree that it would be better to specify it as undefined only when literal is used inside of union. I will see if I can improve the types.

from valibot.

bug-brain avatar bug-brain commented on September 21, 2024

How would I model the type Record<"a" | "b", number>? I would have expected v.record(v.union([v.literal("a"), v.literal("b")]), v.number()) but that makes the keys optional and adds undefined as you mentioned. The function required only works on ObjectSchema.

from valibot.

fabian-hiller avatar fabian-hiller commented on September 21, 2024

I would use object and not record:

import * as v from 'valibot';

const Schema = v.object({ a: v.number(), b: v.number() });

But maybe you are right and our current typing and implementation is wrong and we should only mark it as optional when optional is used for the value of the record.

from valibot.

fabian-hiller avatar fabian-hiller commented on September 21, 2024

@bug-brain I investigated this issue further. It is true that the behavior of Valibot is different from TypeScript and it would be nice to unify it. But there is a tradeoff. To support this behavior, record has to "understand" the schemas used and apply additional logic to ensure that every required entry is defined. This would increase the raw bundle size of the record function from 408 bytes to about 460 bytes (13%). Furthermore, this change would require us to limit the record key schema to string and picklist, as it would require much more code to handle other more "unpredictable" schemas such as union. That's probably also why Zod implemented it this way.

from valibot.

fabian-hiller avatar fabian-hiller commented on September 21, 2024

Since many Zod users are also complaining about this problem in #2623, I will investigate this further to find an appropriate solution.

from valibot.

fabian-hiller avatar fabian-hiller commented on September 21, 2024

I think I fixed it! Will test and investigate my code tomorrow. Thanks for bringing this up!

from valibot.

fabian-hiller avatar fabian-hiller commented on September 21, 2024

Even if I found a solution, I am not sure if we should go this way as a library focused on bundle size. Supporting this behavior increases the bundle size of record, while the same behavior can already be achieved with object. Maybe we should limit the key argument of record to just string (or stick to the current behaviour with undefined) and provide a util function to convert a list of keys into the entries argument of object.

// `record` does only allow `string` as first argument
const Schema1 = v.record(v.string(), v.number());

// For explicit keys, `object` with `entriesFrom` is used instead
const Schema2 = v.object(v.entriesFrom(['foo', 'bar'], v.number()));

// Or without the util function
const Schema2 = v.object({ foo: v.number(), bar: v.number() });

from valibot.

macmillen avatar macmillen commented on September 21, 2024

@fabian-hiller may I ask how much record would increase in bundle size?

from valibot.

macmillen avatar macmillen commented on September 21, 2024

I am using a graphql-codegen-typescript-validation-schema to generate zod enums like this:

export const PosTypeEnumSchema = z.enum(['external', 'online', 'standard', 'telephone']);

That way I don't need to define all of my enum schemas manually.

Now I need a record with these enums as a key type and I don't want to re-define these keys by hand but instead use the generated z.enum. (the alternatives you showed unfortunately don't solve that issue)

from valibot.

fabian-hiller avatar fabian-hiller commented on September 21, 2024

I am not a maintainer of Zod, so I cannot speak for it. I recommend asking the Zod team directly.

from valibot.

fabian-hiller avatar fabian-hiller commented on September 21, 2024

The next version of Valibot will allow you to write the following code to solve your problem

This is now implemented and available.

from valibot.

ksv90 avatar ksv90 commented on September 21, 2024

Good time!
@fabian-hiller, the problem is still relevant for me.
We cannot legally make numbers or symbols the keys of an object. entriesFromList accepts an array of strings.
Therefore, you have to make crutches (I have attached the minimum code below). But in this case, the keys become optional. How can I fix this?

Sorry, but my opinion is that there is no need to come up with additional constructions, such as entriesFromList, when a similar utility from typescript contradicts the behavior of "record". No matter how difficult it may be, you need to try to do it correctly.

import { InferOutput, number, picklist, pipe, record, string, transform } from 'valibot';

export const rows = [0, 1, 2, 3, 4] as const;

// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
export const isRow = (input: any): input is (typeof rows)[number] => rows.includes(input);

export const RowScheme = picklist(rows);

export type Row = InferOutput<typeof RowScheme>;
// type Rows = 0 | 1 | 2 | 3 | 4

/*
  The input type for a "record" must be a string, although the output type can be a number or a symbol.
  Therefore, you have to do redundant transformation.
*/
export const RowNumberSchema = pipe(
  string(),
  transform<string, (typeof rows)[number]>((input) => {
    if (isRow(input)) return input;
    throw new Error(`${input} cannot be assigned to rows`);
  }),
);

// similar type to Row
export type RowNumber = InferOutput<typeof RowNumberSchema>;
// type RowsNumber = 0 | 1 | 2 | 3 | 4

export const RowOptionsSchema = record(RowNumberSchema, number());

export type RowOptions = InferOutput<typeof RowOptionsSchema>;
/*
  type RowOptions = {
    0?: number | undefined;
    1?: number | undefined;
    2?: number | undefined;
    3?: number | undefined;
    4?: number | undefined;
  }
*/

from valibot.

fabian-hiller avatar fabian-hiller commented on September 21, 2024

We cannot legally make numbers or symbols the keys of an object. entriesFromList accepts an array of strings.

I think we can extend it to numbers and symbols. I will investigate this.

Sorry, but my opinion is that there is no need to come up with additional constructions, such as entriesFromList, when a similar utility from typescript contradicts the behavior of "record". No matter how difficult it may be, you need to try to do it correctly.

This is an open source project. You are free to investigate the implementation and provide a proof of concept that aligns with our philosophy. If everything looks good, I will be happy to merge your changes.

from valibot.

fabian-hiller avatar fabian-hiller commented on September 21, 2024

v0.36.0 is available

from valibot.

Related Issues (20)

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.