Schema validation
Suppose we want to validate arbitrary JSON objects that are sent to a web server from a messaging application. One possibility is to write a type predicate that performs some runtime validation on the object.
The first thing to notice is that sending the string null
to our server causes unexpected behavior. Rather than throw an Error
, it continues to the step of validating whether result.sender
is a string. This is because typeof null
returns "object"
- a subtle bug that is difficult to spot.
This method will also grow cumbersome as the interface for Message
grows larger. By maintaining a separate interface and type predicate function for validating that interface, we are effectively replicating an error-checking process between compile time and runtime. With some sophisticated type definitions, we can actually write an object that simultaneously produces a TypeScript interface and runtime type checker.
Using type identifiers
The first step is to define some unique type identifiers. This example uses strings that closely resemble the value of typeof
, but you can use any identifier that TypeScript can distinguish.
We can add more unique identifiers to SchemaType
and expand the ternary logic for the SchemaValue
type to include them. Notice that we are including never
as the final result if none of the types were matched.
- creating a Schema object
- tying it all together into a validator
- deep inference on nested objects and types
Creating a Schema
object
Let's define a Schema
interface to store the data representation of a TypeScript type. For the sake of clarity, there are only four types - it is left as an exercise for the reader to add more.