jmattheis / goverter

Generate type-safe Go converters by simply defining an interface
https://goverter.jmattheis.de/
MIT License
502 stars 47 forks source link

Support `any` type alias #110

Closed phm07 closed 9 months ago

phm07 commented 10 months ago

Take this code snippet:

package example

import "fmt"

// goverter:converter
type Converter interface {
    // goverter:map Property Property | AnyToString
    AToB(A) B
}

func AnyToString(source any) string {
    return fmt.Sprint(source)
}

type A struct {
    Property interface{}
}

type B struct {
    Property string
}

Goverter will fail to generate with the following error message:

Error while creating converter method:
    func (example.Converter).AToB(example.A) example.B

| example.A
|
|      | interface{}
|      |
source.Property
target.Property
|      |
|      | string
|
| example.B

Error using method:
    func example.AnyToString(source any) string

Method source type mismatches with conversion source: any != interface{}

This is problematic since some APIs still use the interface{} type while the Go standard library now only uses the any type alias. This means that in some cases you have to write conversion methods that take interface{}s as arguments and wrap the corresponding standard library methods.

Fixing this would probably mean fully supporting type aliases. I don't know how complex the internal implementation for that would be, so maybe handling the special case for any would suffice as it's the most used type alias in Go.

jmattheis commented 10 months ago

Good catch, generally goverter handles typealias fairly well, because they get automatically normalized to the actual type. any seems like a special type alias, because it isn't exactly interface{} from a type point of view.

When directly specifying methods, goverter should check if the types are assignable. So this bug fix should include the "feature" of using interfaces in custom mapping methods. Example:

import (
    "fmt"
    "time"
)

// goverter:converter
type Converter interface {
    // goverter:map Value | ToString
    Convert(source Input) Output
}

type Input  struct { Value time.Time }
type Output struct { Value string }
func ToString(s fmt.Stringer) string {
    return s.String()
}
jmattheis commented 9 months ago

Fixed in v1.3.0.