prevent Role from forming malformed intersections#205
Merged
Eyas merged 1 commit intogoogle:mainfrom Aug 21, 2025
Merged
Conversation
Collaborator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The Role type (introduced here) is designed to be an intermediary type for all fields and takes the form:
The idea being that any field such as
member: SchemaValue<Person, "member">can be expressed either as its inherent type (Person) or the intermediate typeRole<Person>such as{ "@context": "https://schema.org", "@type": "Organization", "name": "Cryptography Users", "member": { "@type": "OrganizationRole", "member": { "@type": "Person", "name": "Alice" }, "startDate": "1977" } }The issue with this (which is an issue in the schema.org definition, not isolated to just this typescript repository) is that there's no guidance on how to resolve conflicts between
RoleBaseand the property field. This is seen clearly in the CreativeWorkSeason type.To simplify the type graph:
If we expand
CreativeWorkSeason.startDateit resolves as follows:At nested level two this creates the malformed type
Date & { startDate: Date }and as a result nested roles will fail to compile. For example:this results in the compiler error
Fortunately, this example probably doesn't exist in the wild. Based on the definition of Role, I don't think it's semantically meaningful to nest roles like this. However, the current implementation breaks tooling. For example, typia a project which statically analyzes the type tree to create validators breaks when it encounters the nonsensical type
Date & { startDate: Date }. I would imagine other tools also may break.Solution
In this fix, we resolve conflicts between
RoleBaseand the modeled field by preferring the modeled field's type (usingOmit). For example:Not only does this improve the type tree by removing infinite Role depth, but it prevents nonsensical types which can break language servers and static analysis tools.
Risks
Breaking Change
This is a breaking change. However, it only impacts projects using at least two levels of Role recursion specifically for fields which conflict with
RoleBase. This includes:and all fields on all types inherited from
ThingBase(becauseRoleextendsThingBase).However, none of those fields were able to build anyway. So there should be no new breakages as a result of this change.
Divergence from schema.org
schema.org provides no guidance on how to address the conflicting definition within
Role. However, based on the examples listed on Role it seems to me that the intent is clearly that Role is not meant to be nested and that the parameterized property should represent the parent type, NOT a malformed intersection.Testing
Tests pass and lint passes.