raml-spec
raml-spec copied to clipboard
Partial inheritance
Version 1.2
For PATCH methods using JSON Merge Patch (RFC7386), it would be extremely handy to inherit partially, which means that each and every inherited field gets the non-required ? attribute. Directly inherited — from direct ancestors in inheritance graph — and indirectly inherited properties will receive the same non-required attribute.
Minimal example
Given the Person definition:
Person:
properties:
firstName: string
lastName: string
birth: datetime
job?: string
wage: number | null
and the PersonPatch definition partly inheriting from Person type:
PersonPatch:
type: Person?
will result in the following expansion:
PersonPatch:
type: object
properties:
firstName?: string
lastName?: string
birth?: datetime
job?: string
wage?: number | null
Partial Nesting
Partial inheritance only applies to the inherited fields, and not to the fields nested in those inherited fields.
Example
Given the WorkingStatus and Person definitions:
WorkingStatus:
properties:
job: string
wage: number | null
Person:
properties:
firstName: string
lastName: string
workingStatus: WorkingStatus
and the PersonPatch definition partly inheriting from Person type:
PersonPatch:
type: Person?
will result in the following expansion:
PersonPatch:
type: object
properties:
firstName?: string
lastName?: string
birth?: datetime
workingStatus?:
job: string
wage: number | null
Note that workingStatus.job and workingStatus.wage remain required.
To settle this limitation, we can define a new operator, ??, in which the inherited type is deeply partial.
Deeply partial example
Given the WorkingStatus and Person definitions:
WorkingStatus:
properties:
job: string
wage: number | null
Person:
properties:
firstName: string
lastName: string
workingStatus: WorkingStatus
and the PersonPatch definition partly inheriting from Person type:
PersonPatch:
type: Person??
will result in the following expansion:
PersonPatch:
type: object
properties:
firstName?: string
lastName?: string
birth?: datetime
workingStatus?:
job?: string
wage?: number | null
Note that workingStatus.job and workingStatus.wage are not required anymore.
Multiple type inheritance
The implications are not yet fully clear to me, so there might be rewording to do here
- When a type
Cinherits from[A, B?], andAandBhave a conflicting field, therequiredattribute will prevail if present onA. The precedence of the required attribute is order-independent. - A type
Cinheriting from[A, B??]withAandBhaving a conflicting field, therequiredattribute will prevail if present onAfor the conflicting field and all its nested fields. The precedence of the required attribute is order-independent.
Edits:
- 1.1 enriched definition
- 1.2 sharper multiple type inheritance definitions + allowance of conflicting types with deep partial multiple inheritance
EDIT Renamed to partial inheritance because it reminds me of Partial<T> typescript generic type definition:
type Partial<T> = {
[P in keyof T]?: T[P];
};
The idea is interesting and seems really useful, but it doesn't seem well defined to me. Can you elaborate a little bit more on the syntax you are proposing, perhaps by expanding your example, specifying which properties of the type 'Person' would be inherited/required
@LuisTCosta I have expanded the definition and will add edit versions on the header for traceability. Feel free to comment :-)
Ah, now I can clearly see the usefulness of Partial Inheritance. It would solve my own issue, https://github.com/raml-org/raml-spec/issues/684. As for the Multiple type inheritance problem, I'm not sure that by
When a type C inherits from [A, B?], and A and B have a conflicting field, the required attribute will prevail if present on A.
you meant that the conflicting field would be required because type A was inherited first, but I believe it would be better to make a conflicting field required if it is required in any non-partial inherited type, independently of the order of inheritance. Either way, it would be great to see the community's opinion on this.
@LuisTCosta Actually, this is what I meant! The precedence of the required attribute is order-independent (I will make that clear). Conflicting types are merged according to the specification:
Multiple inheritance is allowed only if the sub-type is still a valid type declaration after inheriting all restrictions from its parent types.
So I would find sensible that required: true is an inherited order-independent restriction.
Also in need of Partial Inheritance! Hope this gets added to spec soon. :)