When working with data structures, it can be helpful to create values that conform to a schema with minimal effort.
For this purpose, the Schema module provides default constructors for various schema types, including Structs, Records, filters, and brands.
Structs
Struct schemas allow you to define objects with specific fields and constraints. The make function can be used to create instances of a struct schema.
Example (Creating Struct Instances)
In some cases, you might need to bypass validation. While not recommended in most scenarios, make provides an option to disable validation.
Example (Bypassing Validation)
Records
Record schemas allow you to define key-value mappings where the keys and values must meet specific criteria.
Example (Creating Record Instances)
Filters
Filters allow you to define constraints on individual values.
Example (Using Filters to Enforce Ranges)
Branded Types
Branded schemas add metadata to a value to give it a more specific type, while still retaining its original type.
Example (Creating Branded Values)
When using default constructors, it is helpful to understand the type of value they produce.
For instance, in the BrandedNumberSchema example, the return type of the constructor is number & Brand<"MyNumber">. This indicates that the resulting value is a number with additional branding information, "MyNumber".
This behavior contrasts with the filter example, where the return type is simply number. Branding adds an extra layer of type information, which can assist in identifying and working with your data more effectively.
Error Handling in Constructors
Default constructors are considered “unsafe” because they throw an error if the input does not conform to the schema. This error includes a detailed description of what went wrong. The intention behind default constructors is to provide a straightforward way to create valid values, such as for tests or configurations, where invalid inputs are expected to be exceptional cases.
If you need a “safe” constructor that does not throw errors but instead returns a result indicating success or failure, you can use Schema.validateEither.
Example (Using Schema.validateEither for Safe Validation)
Setting Default Values
When creating objects, you might want to assign default values to certain fields to simplify object construction. The Schema.withConstructorDefault function lets you handle default values, making fields optional in the default constructor.
Example (Struct with Required Fields)
In this example, all fields are required when creating a new instance.
Example (Struct with Default Value)
Here, the age field is optional because it has a default value of 0.
Lazy Evaluation of Defaults
Defaults are lazily evaluated, meaning that a new instance of the default is generated every time the constructor is called:
Example (Lazy Evaluation of Defaults)
In this example, the timestamp field generates a new value for each instance.
Reusing Defaults Across Schemas
Default values are also “portable”, meaning that if you reuse the same property signature in another schema, the default is carried over:
Example (Reusing Defaults in Another Schema)
Using Defaults in Classes
Default values can also be applied when working with the Class API, ensuring consistency across class-based schemas.