jinzhu / copier

Copier for golang, copy value from struct to struct and more
MIT License
5.58k stars 489 forks source link

fix: type converter for interface types #154

Closed aisbergg closed 2 years ago

aisbergg commented 2 years ago

TL;DR

This PR makes it possible to use custom type converters on interface types. It works by resolving interfaces to concrete types before the conversion takes place.


It seems to be impossible to define use interface types for SrcType or DstType of the type converter. Internally the copier will resolve these types to the actual underlying types, because of how reflect.TypeOf works, e.g.:

type MyI interface{ A() }
type MyS struct{}
func (MyS) A() {}

var m MyI = MyS{}
fmt.Println(reflect.TypeOf(m))  // prints: MyS

When the copier encounters a struct field with an interface type, it then wouldn't know what type converter to use. This PR fixes this issue, so that this actually works:

package main

import (
    "fmt"

    "github.com/jinzhu/copier"
)

type Logger interface {
    LogAction(string)
}

type EmployeeLogger struct {
    Level string
}

func (l *EmployeeLogger) LogAction(action string) {}

type Employee struct {
    Logger Logger
}

func main() {
    employee := Employee{Logger: &EmployeeLogger{Level: "debug"}}
    converters := []copier.TypeConverter{
        copier.TypeConverter{
            SrcType: &EmployeeLogger{},
            DstType: &EmployeeLogger{},
            Fn: func(src interface{}) (interface{}, error) {
                return &EmployeeLogger{Level: src.(*EmployeeLogger).Level + "-xxx"}, nil
            },
        },
    }

    copy := Employee{}
    copier.CopyWithOption(&copy, &employee, copier.Option{Converters: converters})
    fmt.Println(copy.Logger.(*EmployeeLogger).Level) // prints: debug-xxx
}