Right now, if we have a type with:
members:
options:
mutable: true
schema:
$interface: "ShapeOptions#SameAsInterface"
...and ShapeOptions is defined using the following (as supported by #18):
components:
interfaces:
# A "oneOf" interface can be used to generate a TypeScript type union or a
# Rust enumeration, including the "Post", etc., variants.
#
# This is not a superclass! It's a type union, using `|` in TypeScript to
# allow more than one type. This affects the design in several surprising
# ways. Among other things, we might have several union types that include
# different concrete interface types in their union. For example,
# `ShapeOptions`, `RoundishShapeOptions`, etc.
#
# We can only create a `oneOf` union of multiple interfaces if the
# individual types _all_ use the same discriminator member (see below).
ShapeOptions:
description: "Options for a shape."
oneOf:
- $interface: "SquareShapeOptions#SameAsInterface"
- $interface: "RoundShapeOptions#SameAsInterface"
SquareShapeOptions:
# The discriminator is a property of `SquareShapeOptions`, even if this
# type appears without being part of `ShapeOptions`. This is because it
# affects how the variants of this type get generated.
discriminatorMemberName: "type"
members:
type:
required: true
initializable: true
schema:
type: string
const: square
height:
required: true
mutable: true
schema:
type: number
width:
required: true
mutable: true
schema:
type: number
RoundShapeOptions:
discriminatorMemberName: "type"
members:
type:
required: true
initializable: true
schema:
type: string
const: round
radius:
required: true
mutable: true
schema:
type: number
...then it is impossible to send a Merge Patch changing .options from:
{
"type": "square",
"height": 10,
"round": 20
}
...to:
{
"type": "round",
"radius": 15
}
This patch necessary for this conversion looks like:
{
"type": "round",
"radius": 15,
"height": null,
"width": null,
}
We need to be able to pass other keys as null, so we can remove the original square parameters when PATCHing type.
There are several strategies we can use here:
- Allow passing any key with
"key": null. We could do this using additionalProperties: { schema: { type: "null" } } }. But we need to check with @blakew about whether this breaks our doc tooling.
- Only allow
height: null and width: null in RoundShapeOptionsMergePatch. But this would require generating a special version of RoundShapeOptionsMergePatch that knows about SquareShapeOptionsMergePatch. Call it "ShapeOptionsRoundShapeOptionsMergePatch". But since oneOf is a type union, not inheritance, we might need several versions of RoundShapeOptionsMergePatch, one for each type union which includes it. So if we have type RoundedShapeOptions = RoundShapeOptions | EllipticalShapeOptions, then we would need RoundedShapeOptionsRoundShapeOptionsMergePatch and RoundedShapeOptionsEllipticalShapeOptionsMergePatch, and so on. This is one of those classic bad ideas that just keeps getting worse.
- Like (2), but we bake
RoundShapeOptionsMergePatch and SquareShapeOptionsMergePatch directly into ShapeOptionsMergePatch. This would require a lot of code, but we might be able to make it nice?
@hanakslr and @blakew, I'd love your thoughts here.
Right now, if we have a type with:
...and
ShapeOptionsis defined using the following (as supported by #18):...then it is impossible to send a Merge Patch changing
.optionsfrom:{ "type": "square", "height": 10, "round": 20 }...to:
{ "type": "round", "radius": 15 }This patch necessary for this conversion looks like:
{ "type": "round", "radius": 15, "height": null, "width": null, }We need to be able to pass other keys as
null, so we can remove the originalsquareparameters when PATCHingtype.There are several strategies we can use here:
"key": null. We could do this usingadditionalProperties: { schema: { type: "null" } } }. But we need to check with @blakew about whether this breaks our doc tooling.height: nullandwidth: nullinRoundShapeOptionsMergePatch. But this would require generating a special version ofRoundShapeOptionsMergePatchthat knows aboutSquareShapeOptionsMergePatch. Call it "ShapeOptionsRoundShapeOptionsMergePatch". But sinceoneOfis a type union, not inheritance, we might need several versions ofRoundShapeOptionsMergePatch, one for each type union which includes it. So if we havetype RoundedShapeOptions = RoundShapeOptions | EllipticalShapeOptions, then we would needRoundedShapeOptionsRoundShapeOptionsMergePatchandRoundedShapeOptionsEllipticalShapeOptionsMergePatch, and so on. This is one of those classic bad ideas that just keeps getting worse.RoundShapeOptionsMergePatchandSquareShapeOptionsMergePatchdirectly intoShapeOptionsMergePatch. This would require a lot of code, but we might be able to make it nice?@hanakslr and @blakew, I'd love your thoughts here.