# Interfaces Avanzadas en Go 2026: Composición, Aserciones de Tipo y Preguntas de Entrevista > Dominar la composición de interfaces en Go, aserciones de tipo y genéricos autorreferenciales de Go 1.26. Incluye ejemplos prácticos y preguntas de entrevista técnica. - Published: 2026-09-10 - Updated: 2026-09-10 - Author: Anthony Fillion-Maillet - Tags: go, interfaces, type-assertions, generics, go-1.26 - Reading time: 12 min --- Las interfaces en Go definen comportamiento sin prescribir implementación, lo que las convierte en un elemento central para escribir código flexible y testeable. Go 1.26 extiende este poder con genéricos autorreferenciales, nuevos iteradores de reflexión y manejo de errores type-safe mediante `errors.AsType`. Esta guía cubre la composición de interfaces, aserciones de tipo, patrones de embedding y las preguntas que surgen en entrevistas técnicas. > **Mejora de Interfaces en Go 1.26** > > Los tipos genéricos ahora pueden referenciarse a sí mismos en su lista de parámetros de tipo: `type Adder[A Adder[A]] interface { Add(A) A }`. Este patrón de polimorfismo F-bounded permite interfaces que restringen los tipos de retorno al tipo que las implementa. ## Composición de Interfaces y Patrones de Embedding El embedding de interfaces combina múltiples interfaces en un solo contrato. El resultado es una nueva interface que requiere todos los métodos de sus interfaces embebidas. Este patrón evita la duplicación y crea límites de abstracción claros. ```go // interfaces.go type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) } type Closer interface { Close() error } // Composed interface embedding three interfaces type ReadWriteCloser interface { Reader Writer Closer } ``` Cualquier tipo que implemente los métodos `Read`, `Write` y `Close` satisface automáticamente `ReadWriteCloser`. La [biblioteca estándar de Go](https://pkg.go.dev/io) utiliza este patrón extensivamente en el paquete `io`. Una pregunta común de entrevista pide a los candidatos explicar por qué Go prefiere interfaces pequeñas y enfocadas. La respuesta está en la composabilidad: `io.Reader` aparece en cientos de funciones porque demanda exactamente un método. Las interfaces más grandes crean un acoplamiento más fuerte y reducen la reutilización. ## Aserciones de Tipo: Sintaxis y Seguridad Las aserciones de tipo extraen un tipo concreto de un valor de interface. La forma de dos valores previene panics devolviendo un booleano que indica el éxito. ```go // assertions.go func processValue(v interface{}) { // Two-value assertion: safe, no panic if str, ok := v.(string); ok { fmt.Printf("String value: %s\n", str) return } // Type switch for multiple types switch val := v.(type) { case int: fmt.Printf("Integer: %d\n", val) case float64: fmt.Printf("Float: %.2f\n", val) case []byte: fmt.Printf("Bytes: %x\n", val) default: fmt.Printf("Unknown type: %T\n", val) } } ``` Los type switches manejan múltiples tipos posibles de manera limpia. Cada caso enlaza `val` al tipo afirmado dentro de su bloque, eliminando la necesidad de aserciones separadas. > **Riesgo de Panic** > > Las aserciones de valor único como `str := v.(string)` causan panic si la aserción falla. El código en producción siempre debe usar la forma de dos valores o un type switch. Los entrevistadores frecuentemente preguntan sobre el rendimiento de las aserciones de tipo. El runtime realiza una única comparación de descriptores de tipo, haciendo las aserciones económicas. El costo aumenta con valores de interface que envuelven punteros a structs grandes debido a la indirección, pero la aserción en sí permanece O(1). ## Genéricos Autorreferenciales en Go 1.26 Go 1.26 introdujo parámetros de tipo autorreferenciales, habilitando interfaces donde los métodos deben retornar el tipo implementador. Este patrón, a veces llamado [polimorfismo F-bounded](https://en.wikipedia.org/wiki/Bounded_quantification#F-bounded_quantification), resuelve una limitación de larga data. ```go // builder.go // Self-referential interface: methods return the same type type Builder[B Builder[B]] interface { WithName(name string) B WithAge(age int) B Build() string } type PersonBuilder struct { name string age int } func (p PersonBuilder) WithName(name string) PersonBuilder { p.name = name return p } func (p PersonBuilder) WithAge(age int) PersonBuilder { p.age = age return p } func (p PersonBuilder) Build() string { return fmt.Sprintf("%s, %d years old", p.name, p.age) } // Generic function using the self-referential constraint func configure[B Builder[B]](b B, name string, age int) string { return b.WithName(name).WithAge(age).Build() } ``` La restricción `Builder[B]` asegura que `WithName` y `WithAge` retornen `B`, no un `Builder` genérico. Sin esto, el tipo de retorno sería la interface, perdiendo la información del tipo concreto y rompiendo el encadenamiento de métodos. Los tipos matemáticos se benefician de este patrón. Una interface `Addable[A Addable[A]]` asegura que `Add(A) A` retorne el mismo tipo numérico, previniendo la mezcla accidental de valores `BigInt` y `Decimal`. ## errors.AsType: Unwrapping de Errores Type-Safe Go 1.26 agregó [`errors.AsType`](https://pkg.go.dev/errors#AsType), reemplazando el patrón de danza de punteros de `errors.As`. La versión genérica retorna el error unwrapped directamente. ```go // errors_handling.go type ValidationError struct { Field string Message string } func (e *ValidationError) Error() string { return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message) } func handleError(err error) { // Go 1.26: generic type-safe unwrapping if valErr, ok := errors.AsType[*ValidationError](err); ok { log.Printf("Validation error on field %s\n", valErr.Field) return } // Before Go 1.26: pointer-based unwrapping // var valErr *ValidationError // if errors.As(err, &valErr) { ... } log.Printf("Unexpected error: %v\n", err) } ``` La nueva API elimina la declaración de variable separada y hace explícito el tipo objetivo en la llamada de función. Las cadenas de errores se buscan de la misma manera que antes; solo cambió la interface. ## Iteradores de Reflexión para Inspección de Interfaces Go 1.26 agregó métodos iteradores al paquete `reflect`. `Type.Methods()` y `Value.Methods()` retornan iteradores para recorrer métodos, reemplazando los bucles basados en índices. ```go // reflection.go import "reflect" func inspectInterface(v interface{}) { t := reflect.TypeOf(v) // Go 1.26: iterator-based method inspection fmt.Printf("Type %s methods:\n", t.Name()) for method := range t.Methods() { fmt.Printf(" %s: %s\n", method.Name, method.Type) } // For struct fields (also new in 1.26) if t.Kind() == reflect.Struct { for field := range t.Fields() { fmt.Printf(" Field: %s (%s)\n", field.Name, field.Type) } } } ``` El patrón de iterador se alinea con la característica range-over-function de Go 1.23. El código se vuelve más legible, y no hay penalización de rendimiento: los iteradores producen valores de manera lazy. ## Satisfacción de Interface en Tiempo de Compilación Go verifica la satisfacción de interface en tiempo de compilación cuando se asigna un valor concreto a una variable de interface. Las verificaciones explícitas usando asignaciones con identificador en blanco detectan errores temprano en bases de código grandes. ```go // compile_check.go type Storage interface { Save(key string, data []byte) error Load(key string) ([]byte, error) Delete(key string) error } type FileStorage struct { basePath string } // Compile-time check: fails if FileStorage misses a method var _ Storage = (*FileStorage)(nil) func (f *FileStorage) Save(key string, data []byte) error { path := filepath.Join(f.basePath, key) return os.WriteFile(path, data, 0644) } func (f *FileStorage) Load(key string) ([]byte, error) { path := filepath.Join(f.basePath, key) return os.ReadFile(path) } func (f *FileStorage) Delete(key string) error { return os.Remove(filepath.Join(f.basePath, key)) } ``` La línea `var _ Storage = (*FileStorage)(nil)` compila solo si `*FileStorage` satisface `Storage`. Este patrón detecta métodos faltantes inmediatamente en lugar de en runtime cuando se asigna un valor. ## La Interface Vacía y Restricciones de Tipo La interface vacía `interface{}` acepta cualquier valor, mientras que `any` es su alias desde Go 1.18. Las restricciones genéricas proporcionan seguridad de tipo en tiempo de compilación sin aserciones en runtime. ```go // constraints.go import "golang.org/x/exp/constraints" // Generic function with numeric constraint func Sum[T constraints.Integer | constraints.Float](values []T) T { var total T for _, v := range values { total += v } return total } // Comparable constraint for map keys func Contains[K comparable, V any](m map[K]V, key K) bool { _, exists := m[key] return exists } ``` Preferir genéricos sobre `interface{}` elimina las aserciones de tipo y detecta incompatibilidades de tipo en tiempo de compilación. La compensación es complejidad añadida en las firmas de funciones, por lo que los genéricos encajan mejor cuando una función genuinamente opera sobre múltiples tipos. ## Preguntas de Entrevista Técnica sobre Interfaces Go Los entrevistadores prueban el conocimiento de interfaces en múltiples niveles. Aquí hay preguntas comunes con respuestas concisas. **P: ¿Qué sucede cuando se llama a un método en un valor de interface nil versus un valor concreto nil dentro de una interface?** Una interface nil no tiene tipo ni valor; llamar a cualquier método causa panic. Una interface no-nil conteniendo un puntero nil tiene un tipo; el método se ejecuta con un receiver nil. Este comportamiento permite patrones como `(*bytes.Buffer)(nil).String()` retornando un string vacío. **P: ¿Cómo funciona la comparación de interfaces?** Dos valores de interface son iguales si tienen el mismo tipo dinámico y valores dinámicos iguales. Comparar interfaces con tipos no comparables (slices, maps, funciones) causa panic en runtime. **P: ¿Cuándo debe un método usar un receiver de puntero versus un receiver de valor?** Los receivers de puntero permiten mutación y evitan copiar structs grandes. Los receivers de valor son seguros para uso concurrente y funcionan tanto con valores como con punteros. Si algún método necesita un receiver de puntero, todos los métodos en ese tipo deberían usar receivers de puntero por consistencia, ya que solo `*T` satisface una interface que requiere un método con receiver de puntero. ```go // receiver_rules.go type Counter struct { count int } // Pointer receiver: modifies the struct func (c *Counter) Increment() { c.count++ } // Value receiver: read-only operation func (c Counter) Value() int { return c.count } type Incrementer interface { Increment() } func main() { var c Counter // var i Incrementer = c // Compile error: Counter lacks Increment var i Incrementer = &c // OK: *Counter has Increment i.Increment() } ``` **P: Explicar el principio "aceptar interfaces, retornar structs".** Las funciones que aceptan interfaces se desacoplan de implementaciones concretas, habilitando pruebas con mocks. Retornar tipos concretos da a los llamadores acceso completo a los métodos del tipo sin aserciones de tipo. Retornar una interface oculta futuras adiciones de métodos detrás de una aserción de tipo. Para más preparación de entrevistas Go, ver el módulo de [preguntas de concurrencia Go](/technologies/go/interview-questions/concurrency-patterns) y el [módulo de testing](/technologies/go/interview-questions/testing). > **Consejo de Entrevista** > > Cuando se pide diseñar una interface, comenzar con el caso de un solo método. Expandir solo cuando múltiples métodos siempre se llaman juntos. Las interfaces `Stringer`, `Reader` y `Handler` de la biblioteca estándar definen cada una un solo método. ## Lo que los Desarrolladores Go Senior Deben Saber sobre Interfaces - La composición de interfaces a través de embedding crea contratos flexibles sin jerarquías de herencia. Mantener las interfaces pequeñas: uno a tres métodos cubre la mayoría de los casos de uso. - Las aserciones de tipo y type switches extraen tipos concretos de manera segura. Siempre usar la forma de dos valores o un switch en código de producción para evitar panics. - Los genéricos autorreferenciales de Go 1.26 habilitan patrones builder y tipos matemáticos donde los métodos deben retornar el tipo implementador. - `errors.AsType` simplifica el unwrapping de errores con una API genérica y type-safe. Usarlo en código nuevo dirigido a Go 1.26 o posterior. - Las verificaciones de interface en tiempo de compilación con `var _ Interface = (*Type)(nil)` detectan métodos faltantes antes de que se ejecuten las pruebas. - Preferir genéricos sobre `interface{}` cuando la seguridad de tipo en tiempo de compilación supera la complejidad en las firmas. - La guía "aceptar interfaces, retornar structs" mantiene las APIs flexibles para los llamadores mientras expone toda la funcionalidad. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/es/blog/go/go-interfaces-advanced-composition-type-assertions-2026