laslowh
laslowh

Reputation: 8594

How to get the reflect.Type of an interface?

In order to determine whether a given type implements an interface using the reflect package, you need to pass a reflect.Type to reflect.Type.Implements(). How do you get one of those types?

As an example, trying to get the type of an uninitialized error (interface) type does not work (it panics when you to call Kind() on it)

var err error
fmt.Printf("%#v\n", reflect.TypeOf(err).Kind())

Upvotes: 41

Views: 38257

Answers (4)

xianyukang
xianyukang

Reputation: 11

Since Go 1.22, we can use the reflect.TypeFor function to get a reflect.Type value:

func main() {
    errorType := reflect.TypeFor[error]()
    err := errors.New("foo")
    println(reflect.TypeOf(err).Implements(errorType))
}

Upvotes: 1

Evan Shaw
Evan Shaw

Reputation: 24547

Do it like this:

var err error
t := reflect.TypeOf(&err).Elem()

Or in one line:

t := reflect.TypeOf((*error)(nil)).Elem()

Upvotes: 57

Kristoffer
Kristoffer

Reputation: 143

Even Shaws response is correct, but brief. Some more details from the reflect.TypeOf method documentation:

// As interface types are only used for static typing, a common idiom to find
// the reflection Type for an interface type Foo is to use a *Foo value.

writerType := reflect.TypeOf((*io.Writer)(nil)).Elem()

fileType := reflect.TypeOf((*os.File)(nil)).Elem()
fmt.Println(fileType.Implements(writerType))

Upvotes: 14

Andrew Burns
Andrew Burns

Reputation: 14479

For googlers out there I just ran into the dreaded scannable dest type interface {} with >1 columns (XX) in result error.

Evan Shaw's answer did not work for me. Here is how I solved it. I am also using the lann/squirrel library, but you could easily take that out.

The solution really isn't that complicated, just knowing the magic combination of reflect calls to make.

The me.GetSqlx() function just returns an instance to *sqlx.DB

    func (me *CommonRepo) Get(query sq.SelectBuilder, dest interface{}) error {
      sqlst, args, err := query.ToSql()
      if err != nil {
        return err
      }
      // Do some reflection magic so that Sqlx doesn't hork on interface{}
      v := reflect.ValueOf(dest)
      return me.GetSqlx().Get(v.Interface(), sqlst, args...)
    }
    func (me *CommonRepo) Select(query sq.SelectBuilder, dest interface{}) error {
      sqlst, args, err := query.ToSql()
      if err != nil {
        return err
      }
      // Do some reflection magic so that Sqlx doesn't hork on interface{}
      v := reflect.ValueOf(dest)
      return me.GetSqlx().Select(v.Interface(), sqlst, args...)
    }

Then to invoke it you can do:

    func (me *myCustomerRepo) Get(query sq.SelectBuilder) (rec Customer, err error) {
      err = me.CommonRepo.Get(query, &rec)
      return
    }
    func (me *myCustomerRepo) Select(query sq.SelectBuilder) (recs []Customer, err error) {
      err = me.CommonRepo.Select(query, &recs)
      return
    }

This allows you to have strong types all over but have all the common logic in one place (CommonRepo in this example).

Upvotes: -1

Related Questions