Apply a schema transformation

What if the Post schema contains a transformation? Let's try this out!

A common transformation is applying a brand to a type using Schema.brand. We can do this for id inside Post:

src/services/schema.ts
export class Post extends Schema.Class<Post>("Post")({
  userId: Schema.Number,
  id: Schema.Number.pipe(Schema.brand("Id")),
  title: Schema.String,
  body: Schema.String,
}) {
  static readonly Array = Schema.Array(this);
}

We are now getting a type error inside index.tsx:

Type 'number' is not assignable to type 'Brand<"Id">'

The encoded type lost the brand, so it is not assignable anymore to the Post type expected as prop.
The encoded type lost the brand, so it is not assignable anymore to the Post type expected as prop.

By using Schema.encode the original Post type lost the brand applied to id, whereas the posts prop type inside Posts is still defined as readonly Post[].

This was not an issue before because the encoded and decoded types were the same (no transformation). However, adding a brand makes the decoded type different from the encoded type.

Therefore, to fix the issue we need to accept the encoded schema as a prop. We can do this by accessing the Posts.Encoded type:

src/components/Posts.tsx
export default function Posts({
  posts,
}: {
  posts: readonly (typeof Post.Encoded)[];
}) {
  return (
    <div>
      {posts.map((post) => (
        <div key={post.id}>
          <h1>{post.title}</h1>
          <p>{post.body}</p>
        </div>
      ))}
    </div>
  );
}

Every Schema exports two types:

  • Schema.Type: the decoded type of the schema (after transformations)
  • Schema.Encoded: the encoded type of the schema (before transformations)

With Schema.Class the class type (Post) represents the decoded type (typeof Post.Type).

With this the type error is gone, and the app is working correctly again.