Type Transformers
Last updated
Last updated
TypeScript allows you to transform the shape of types via special generics like Partial<...>
, as well as special syntax, like . Moat Maker chose to go a simpler and more lightweight route of allowing you to write arbitrary code to transform a validator type.
When a validator instance is created, the "rules" it follows can be found in the ruleset
property of the validator instance. A ruleset is an object of type Ruleset
that contains two fields, a rootRule
property, which is the root node of a tree of rules, and an interpolated
property, which contains a list of everything that was interpolated into the template. The entire ruleset is deeply frozen to prevent mutation.
A transformer is simply a function that is capable of transforming a ruleset into another ruleset. You can then feed your transformed ruleset into validator.fromRuleset()
to produce a new validator instance patterned after those rules.
Stability Warning: Treat type transformers as a last resort. They tend to be somewhat fragile and may break between updates. As new features come out, additional rule types may be added, and new fields could be added to existing rule types, or worse, the shape of existing rules will have to be changed. These changes can cause existing transformers to either completely break, or to not properly fulfill their role anymore.
We are going to make a transformer that, when given a validator that validates an array of X, it will extract the X and turn that into a new validator. For example, typeOfArrayContent(validator`string[]`)
will be equivalent to validator`string`
.
Let's first take a peek at what an array rule looks like. Documentation for information like this can be found under "Rule Structure" sections in . We can also simply create a validator instance ourselves, check the ruleset
property, and see what comes out.
This shows us that the shape of an array rule is fairly simple. It will have a category property set to "array"
, and a content property set to an arbitrary rule. Thus, a transformer will simply need to:
verify that the passed-in ruleset represents an array
Extract the "content" from the array rule
Built the new ruleset. Make sure to preserve the interpolated array, in case the extracted content needs those interpolated values.
Here's what the end result looks like:
In this scenario, we chose to make the transformer accept a ruleset as an argument. Don't take this as some established convention. You can choose to make them take a validator instance as an argument, and have it return a new validator. Or, if it makes sense, you can choose to make it take a rule (instead of a ruleset) as an argument.