Validator test case details

The test case data structure type looks like this:

export type 
type User = {
    name: UserName;
    email: Email;
    createdAt: Date;
    updatedAt: Date;
    subscription: SubscriptionType;
    stripeId: StripeId;
    visits: Visits;
    favouriteColours: Set<Colour>;
    profile: Profile;
    fileSystem: FileSystemItem;
}
User
= {
name: stringname: type UserName = stringUserName; email: stringemail: type Email = stringEmail; createdAt: DatecreatedAt: Date; updatedAt: DateupdatedAt: Date; subscription: SubscriptionTypesubscription: type SubscriptionType = "pro" | "basic" | "free"SubscriptionType; stripeId: `cus_${string}`stripeId: type StripeId = `cus_${string}`StripeId; visits: numbervisits: type Visits = numberVisits; favouriteColours: Set<Colour>favouriteColours: interface Set<T>Set<type Colour = PresetColours | `#${string}`Colour>; profile: Profileprofile:
type Profile = {
    type: 'listener';
    boughtTracks: NonNegativeInteger;
} | {
    type: 'artist';
    publishedTracks: NonNegativeInteger;
}
Profile
;
fileSystem: FileSystemItemfileSystem:
type FileSystemItem = {
    name: FileName;
} & ({
    type: 'directory';
    children: FileSystemItem[];
} | {
    type: 'file';
})
FileSystemItem
;
}; type type SubscriptionType = "pro" | "basic" | "free"SubscriptionType = 'pro' | 'basic' | 'free'; type
type FileSystemItem = {
    name: FileName;
} & ({
    type: 'directory';
    children: FileSystemItem[];
} | {
    type: 'file';
})
FileSystemItem
= {
name: stringname: type FileName = stringFileName; } & ( | { type: "directory"type: 'directory'; children: FileSystemItem[]children:
type FileSystemItem = {
    name: FileName;
} & ({
    type: 'directory';
    children: FileSystemItem[];
} | {
    type: 'file';
})
FileSystemItem
[];
} | { type: "file"type: 'file'; } ); type
type Profile = {
    type: 'listener';
    boughtTracks: NonNegativeInteger;
} | {
    type: 'artist';
    publishedTracks: NonNegativeInteger;
}
Profile
=
| { type: "listener"type: 'listener'; boughtTracks: numberboughtTracks: type NonNegativeInteger = numberNonNegativeInteger; } | { type: "artist"type: 'artist'; publishedTracks: numberpublishedTracks: type NonNegativeInteger = numberNonNegativeInteger; }; // every type here is to be checked and nominal-typed by validators type type NonEmptyString = stringNonEmptyString = string; type type UserName = stringUserName = type NonEmptyString = stringNonEmptyString; type type Email = stringEmail = type NonEmptyString = stringNonEmptyString; type type StripeId = `cus_${string}`StripeId = `cus_${string}`; type type NonNegativeInteger = numberNonNegativeInteger = number; type type Visits = numberVisits = type NonNegativeInteger = numberNonNegativeInteger; type type PresetColours = "red" | "green" | "blue"PresetColours = 'red' | 'green' | 'blue'; type type HexColour = `#${string}`HexColour = `#${string}`; type type Colour = PresetColours | `#${string}`Colour = type PresetColours = "red" | "green" | "blue"PresetColours | type HexColour = `#${string}`HexColour; type type FileName = stringFileName = string;

The "abstract" features checked are:

  • Any basic features you’d expect from a validator: “This string is a string, this object has fields I expect…”
  • Products and sums (algebraic data types)
  • Nominal types: telling apart two primitives (e.g. strings) of different meaning (e.g. UserId and PostId)
  • "Codecs" semantics support: not only "validate an unknown value" but also "encode the validated value back [to JSON]"
  • Transformation support: interpretation of network-serializable types as environment-specific types, e.g. string[] as Set<string>
  • Representation of recursive types, e.g. tree
  • Importantly, any of those features in combination with each other

Specifically, the tests are:

  • switchDates: Switches the createdAt and updatedAt fields
  • prefixCustomerId: Adds an invalid prefix to the stripeId field
  • addTwoAtsToEmail: Renders the email invalid by adding two @s
  • clearName: Clears the name field
  • addFavouriteTiger: Adds an invalid colour to the favouriteColours field
  • addFavouriteRed: Adds a duplicated valid colour to the favouriteColours field
  • setSubscriptionTypeBanana: Sets the subscription field to banana
  • setHalfVisits: Renders the visits field to be a float instead of an integer
  • setCreatedAtCyborgWar: Sets invalid createdAt date
  • setProfileArtist: Sets the valid profile field to an invalid structure
  • addFileSystemUFOType: An enum test not unlike the Tiger test, but in composition with recursive data structures
  • addFileSystemDupeFile: Adds a duplicated value to the tree. My tree has the “unique list” semantics, so that shouldn’t be possible
  • branded: Branded types are supported
  • typedErrors: Typed errors are supported
  • templateLiterals: Template literals are supported
  • emailFormatAmbiguityIsAccountedFor: Email format ambiguity is accounted for either in API or in Docs. The library doesn't promise not being able to deliver
  • acceptsTypedInput: The library accepts not only unknown/any types as validation input, but more refined "intermediate" types as well
  • canGenerateJsonSchema: Whether the schema itself can be serialized to cross-system communication. Became more relevant with OpenAI introducing structured outputs
  • encodedEqualsInput: Can encode the value into another value (mainly, back to a serializable format), vs. only decoding it
  • transformationsPossible: Transformations are possible

Most of the tests are performed automatically, the others you have to take my word for it (or check the code). PRs are welcome.

More detailed test case description and more context can be found in my blog post.