获取未初始化切片的类型

I want to return a type of an interface{}, while the input value might be var m []*MyModel

I've managed to get to the type *MyModel, while MyModel not as a pointer seems to be unreachable to me.

func getType( m interface{} ) reflect.Type {

    t := reflect.TypeOf( m );   
    v := reflect.ValueOf( m );

    if t.Kind() == reflect.Ptr {    

        if v.IsValid() && !v.IsNil() {          
            return getType( v.Elem().Interface() );         
        }

        panic( "We have a problem" );   

    }

    if t.Kind() == reflect.Slice {

        if v.Len() == 0 {           

            s := reflect.MakeSlice( t , 1 , 1 );            
            return getType( s.Interface() );    

        }

        return getType( v.Index( 0 ).Interface() );

    }

    return t;

}

Is it possible?

You may use Type.Elem() to get the type's element type, which works for Array, Chan, Map, Ptr and Slice.

You may run a loop and "navigate" to the type's element type until the type is not a pointer nor a slice (nor array, chan, map if you need so).

So the simple solution is this:

func getElemType(a interface{}) reflect.Type {
    for t := reflect.TypeOf(a); ; {
        switch t.Kind() {
        case reflect.Ptr, reflect.Slice:
            t = t.Elem()
        default:
            return t
        }
    }
}

Testing it:

type MyModel struct{}

fmt.Println(getElemType(MyModel{}))
fmt.Println(getElemType(&MyModel{}))
fmt.Println(getElemType([]MyModel{}))
fmt.Println(getElemType([]*MyModel{}))
fmt.Println(getElemType(&[]*MyModel{}))
fmt.Println(getElemType(&[]****MyModel{}))
fmt.Println(getElemType(&[][]**[]*[]***MyModel{}))
var p *[][]**[]*[]***MyModel
fmt.Println(p) // It's nil!
fmt.Println(getElemType(p))

Output (try it on the Go Playground):

main.MyModel
main.MyModel
main.MyModel
main.MyModel
main.MyModel
main.MyModel
main.MyModel
<nil>
main.MyModel

As you can see, no matter how "deep" we go with slices and pointers (&[][]**[]*[]***MyModel{}), getElemType() is able to extract main.MyModel.

One thing to note is that in my solution I used reflect.Type and not reflect.Value. Go is a statically typed language, so the type information is there even if pointers and slice elements are not "populated", even if we pass a "typed" nil such as p, we're still able to navigate through the "type chain".

Note: The above getElemType() panics if called with an untyped nil value, e.g. getElemType(nil), because in this case there is no type information available. To defend this, you may add a simple check:

if a == nil {
    return nil
}

Note #2: Since the implementation contains a loop without limiting iteration count, values of recursive types will drive it into an endless loop, such as:

type RecType []RecType
getElemType(RecType{}) // Endless loop!