Coder Social home page Coder Social logo

Comments (5)

sinclairzx81 avatar sinclairzx81 commented on June 4, 2024 1

@pterrien Hiya,

I guess a workaround for me would be to build an "empty" initial value, depending on the base type of my schema, something like:

Yeah, while possible, this is somewhat working against the grain. While the Default function is written to generate values users may have missed, there is still potential for values to be invalid, and there's a line between what can be reasonably generated and what users "should" pass for an value to be considered valid. For example, if a user passes undefined instead of an object, and the validator simply constructs an object, was there any reason for the user to pass anything at all?

This said, you can control what gets generated by setting default annotations at each level of the type.

import { Type } from '@sinclair/typebox'
import { Value } from '@sinclair/typebox/value'

const T = Type.Object({
  vector: Type.Object({
    x: Type.Number({ default: 1 }),
    y: Type.Number({ default: 2 }),
    z: Type.Number({ default: 3 }),  
  }, { default: {} }),
  normal: Type.Object({
    x: Type.Number({ default: 1 }),
    y: Type.Number({ default: 2 }),
    z: Type.Number({ default: 3 }),  
  }, { default: {} })
}, { default: {} })

const R = Value.Default(T, undefined)

console.log(R) // { vector: { x: 1, y: 2, z: 3 }, normal: { x: 1, y: 2, z: 3 } }

TypeBox will generate values at each level level, expanding out to the complete value. By omitting a default annotation at a higher level, this will prevent Default from initializing a value (and may result in error on decode if the user hasn't provided correct sub values (which is a good thing)). The rules around how applications want to approach value initialization are very nuanced, and TypeBox can't really assume anything, but you should be able to get some use out of Default based on the above.

Additionally, user defined Value functions (implemented entirely outside of Value.* using schema introspection) are encouraged, so if you do need something specific, you can implement functions from scratch (just check the way TB implements the current Value.* functions for an example on how to approach that (most are written using visitor patterns))

Again, hope this helps!
S

from typebox.

sinclairzx81 avatar sinclairzx81 commented on June 4, 2024

@pterrien Hi,

Question: Is there a way to not apply these implicit defaults? Something like a Value.CreateStrict(), that would ultimately fail on Value.Decode(), similarly to Value.Default()?

The Create function is intended to instantiate a value that matches the given type, so it does need to initialize implicit defaults. The same is true for Cast. Keep in mind that Create and Cast are usually not very helpful for validation (they are more intended for data migration scenarios where you may need to instance (and fix) values due to schematic changes; this is particularly true for Cast)

The Default function however is intended for use in validation so would recommend using this if the goal is to validate. The Default function can pre-process a value prior to validation (Decode) by applying defaults for any default annotated type,; a lot of server frameworks use it for this purpose. The function actually replicates the Ajv useDefaults https://ajv.js.org/options.html#usedefaults configuration if you're familiar with it.

Um, I'm not too sure what to suggest here except perhaps consider avoiding Create, Cast during validation passes (as Default, Clean and Convert are typically the functions to use for pre-processing values). Any additional processing above Default, Clean and Convert would likely need to be custom....

export function Parse<T extends TSchema>(schema: T, value: unknown) {
  const converted = Value.Convert(T, value)
  const defaulted = Value.Default(T, converted)
  const cleaned = Value.Clean(T, defaulted)
  // todo: additional custom processing of the value here.
  return Value.Decode(schema, cleaned)
}

The above is the intended usage for these functions and will error if the value is incorrect by the time it reaches the Decode call. It should be possible to apply custom logic prior to the Decode call if you need it.

Hope this helps
S

from typebox.

pterrien avatar pterrien commented on June 4, 2024

Hi,

Thank you for your very quick reply.
I actually use the Parse() function, that I copied from issue #726 . I restricted my example code to Value.Decode(T, Value.Default(T, value)), just for the sake of clarity.
My problem being that, if I pass an undefined value to it, the error message is simply 'Expected object', because undefined will not get coerced to an Object.

I guess a workaround for me would be to build an "empty" initial value, depending on the base type of my schema, something like:

let initValue
if (
	Type.Extends(T, Type.Object({}), Type.Literal(true), Type.Literal(false)).const
) {
	initValue = {}
} else if (
	Type.Extends(T, Type.Array(Type.Unknown()), Type.Literal(true), Type.Literal(false)).const
) {
	initValue = []
} else {
	initValue = undefined
}
const invalid3 = Parse(T, initValue) // correctly throws an error, with message='Required property` and path='/per'

Not the most elegant, and surely not bullet-proof with exotic schemas (intersect, union, ...), but in my use case I guess it should be acceptable as long as I use fairly standard schemas.

from typebox.

pterrien avatar pterrien commented on June 4, 2024

TypeBox can't really assume anything, but you should be able to get some use out of Default based on the above.

This totally makes sense, and using default on the object is indeed far more rigorous and elegant than my ugly workaround... :)

if you do need something specific, you can implement functions from scratch (just check the way TB implements the current Value.* functions for an example on how to approach that (most are written using visitor patterns))

I will definitely have a look, even though using default will probably cover my needs.

Again, hope this helps!

A lot, I sincerely thank you for your kind help!

from typebox.

sinclairzx81 avatar sinclairzx81 commented on June 4, 2024

@pterrien All good :)

Will go ahead and close up this issue for now.

All the best!
S

from typebox.

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.