[SOLVED] Issue with Annotations in Schema with Identifiers and Potential Workarounds

I found a footgun in Schema that hasn't been obvious to me until now: It's that adding annotations to a schema that already has an identifier annotation is fundamentally different from adding annotations to one that doesn't.

For example, I can create a struct like this:

const Message = S.Struct({
  userId: S.String.annotations({
    description: 'The ID of the user who posted the message'
  })
});


And the resulting JSONSchema will correctly associate the description with this one particular instance of String, because String in itself doesn't have an identifier. But say I do this:

const UserId = S.String.annotations({identifier: 'UserId'});

const User = S.Struct({
  id: UserId.annotations({
    description: 'The unique identifier of the user'
  })
});

const Message = S.Struct({
  userId: UserId.annotations({
    description: 'The ID of the user who posted the message'
  })
});

const UserMessage = S.Struct({
  message: Message,
  user: User,
});

console.dir(JSONSchema.make(UserMessage), {depth: Infinity});


Then in the resulting JSONSchema, the $defs.UserId.description property has "The ID of the user who posted the message", and so if you'd look at properties.user.id you'd be scratching your head. What message is it talking about??

It seems that the first description encountered "wins". If I swap the order of
message
and user in the UserMessage struct, then the description becomes "The unique identifier of the user", which is too generic for properties.message.userId.

How does one work around this? I see two options. Is there a better way?

1. Give every property a unique identifier
2. Wrap properties in S.Union(T) before adding annotations, so the resulting oneOf carries the context-specific annotations
Was this page helpful?