Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go: Use private interfaces instead of generating structs for discriminating types #67

Open
diamondburned opened this issue Feb 13, 2023 · 1 comment

Comments

@diamondburned
Copy link

diamondburned commented Feb 13, 2023

Proposal

This issue proposes that for the following JSON type definition:

{
  "Thing": {
    "discriminator": "type",
    "mapping": {
      "a": {
        "properties": {}
      },
      "b": {
        "properties": {}
      },
      "c": {
        "properties": {}
      }
    }
  }
}

jtd-codegen should generate the following Go code:

type Thing struct {
	Type string `json:"type"`

	// Value can be the following types:
	//
	//    - [A]
	//    - [B]
	//    - [C]
	//
	Value thingValue `json:"-"`
}

func (t *Thing) UnmarshalJSON(data []byte) error
func (t *Thing) MarshalJSON() ([]byte, error)

type thingValue interface {
	isThing()
}

type ThingA struct{}
type ThingB struct{}
type ThingC struct{}

func (ThingA) isThing() {}
func (ThingB) isThing() {}
func (ThingC) isThing() {}

The user would consume the API like so:

var t Thing

switch v := t.Value.(type) {
case ThingA:
	log.Println("thing contains type A")
case ThingB:
	log.Println("thing contains type B")
case ThingC:
	log.Println("thing contains type C")
default:
	log.Println("thing contains unknown type")
}

If the user makes a mistake in the type-switch, the compiler will complain:

var t Thing

switch v := t.Value.(type) {
case ThingA:
	log.Println("thing contains type A")
case string:
	// does not even compile
}

This issue is an alternative to issue #49.

Rationale

Pros:

  • Using interfaces instead of flat structs can sometimes takes up less space in
    memory if the mapping is large.
  • The user can use type-switches to determine the type of the value, which is
    more idiomatic and safer in Go than using a string field.

Cons:

  • The magic field's name Value is not ideal. This is done because we can't
    have custom marshalers and unmarshalers on an interface.
  • The magic field has an unexported type. Showing an unexported type in an
    exported type is not ideal.
  • It's not explicit which types actually satisfy TValue outside a single
    comment. This might be confusing to users.
@diamondburned
Copy link
Author

If I can get this project to build locally in Nix, I will probably file a PR for this.

diamondburned added a commit to diamondburned/json-typedef-codegen that referenced this issue Feb 13, 2023
diamondburned added a commit to diamondburned/json-typedef-codegen that referenced this issue Feb 13, 2023
diamondburned added a commit to diamondburned/json-typedef-codegen that referenced this issue Feb 28, 2023
diamondburned added a commit to diamondburned/json-typedef-codegen that referenced this issue Feb 28, 2023
diamondburned added a commit to diamondburned/json-typedef-codegen that referenced this issue Feb 28, 2023
diamondburned added a commit to diamondburned/json-typedef-codegen that referenced this issue Mar 1, 2023
diamondburned added a commit to diamondburned/json-typedef-codegen that referenced this issue Mar 1, 2023
diamondburned added a commit to diamondburned/json-typedef-codegen that referenced this issue Mar 2, 2023
diamondburned added a commit to diamondburned/json-typedef-codegen that referenced this issue Mar 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant