raml-spec icon indicating copy to clipboard operation
raml-spec copied to clipboard

Partial inheritance

Open jsamr opened this issue 7 years ago • 6 comments

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 C inherits from [A, B?], and A and B have a conflicting field, the required attribute will prevail if present on A. The precedence of the required attribute is order-independent.
  • A type C inheriting from [A, B??] with A and B having a conflicting field, the required attribute will prevail if present on A for 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

jsamr avatar Aug 06 '18 23:08 jsamr

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];
};

jsamr avatar Aug 09 '18 14:08 jsamr

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 avatar Aug 13 '18 14:08 LuisTCosta

@LuisTCosta I have expanded the definition and will add edit versions on the header for traceability. Feel free to comment :-)

jsamr avatar Aug 13 '18 15:08 jsamr

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 avatar Aug 18 '18 15:08 LuisTCosta

@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.

jsamr avatar Aug 18 '18 15:08 jsamr

Also in need of Partial Inheritance! Hope this gets added to spec soon. :)

CoderSpinoza avatar Aug 31 '18 02:08 CoderSpinoza