Skip to content

Struct Embedding

TypeScript lets you extend classes with extends (single inheritance) and approximate multiple inheritance through mixins. The mental model is a hierarchy: child IS-A parent.

Go has no classes, no inheritance. Instead it uses embedding — a type can include another type as an anonymous field, and the embedded type’s methods and fields are promoted to the outer type. The mental model is composition: outer HAS-A inner.

Declare an embedded type by naming it without a field name inside the struct body.

TypeScript
// TypeScript — class inheritance
class Animal {
name: string;
constructor(name: string) { this.name = name; }
speak(): string { return `${this.name} makes a sound`; }
}
class Dog extends Animal {
breed: string;
constructor(name: string, breed: string) {
super(name);
this.breed = breed;
}
speak(): string { return `${this.name} barks`; }
}
const d = new Dog("Rex", "Labrador");
console.log(d.speak()); // Rex barks
console.log(d.name); // Rex — inherited
Go
// Go — struct embedding (composition)
type Animal struct{ Name string }
func (a Animal) Speak() string {
return a.Name + " makes a sound"
}
type Dog struct {
Animal // embedded — no field name
Breed string
}
func (d Dog) Speak() string {
return d.Name + " barks" // d.Name promoted from Animal
}
func main() {
d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
fmt.Println(d.Speak()) // Rex barks
fmt.Println(d.Animal.Speak()) // Rex makes a sound
fmt.Println(d.Name) // Rex — promoted field
}

When a method exists only on the embedded type (not overridden), it is promoted automatically to the outer type’s method set. This lets the outer type satisfy interfaces implemented by the inner type.

TypeScript
// TypeScript — mixin pattern for reuse
type Serializable = {
serialize(): string;
};
function withSerialize<T extends object>(Base: new(...a: any[]) => T) {
return class extends Base implements Serializable {
serialize() { return JSON.stringify(this); }
};
}
Go
type Logger struct{ Prefix string }
func (l Logger) Log(msg string) {
fmt.Println(l.Prefix+":", msg)
}
type UserService struct {
Logger // promoted: UserService.Log() works
repo UserRepository
}
// UserService now has .Log() without re-implementing it
func (s UserService) CreateUser(name string) {
s.Log("creating user: " + name)
// ... business logic
}

Because promoted methods count toward the outer struct’s method set, embedding is commonly used to satisfy interfaces without boilerplate delegation.

TypeScript
// TypeScript — implement interface manually
interface Writer {
write(data: string): void;
}
class BufferedWriter implements Writer {
private inner: Writer;
constructor(inner: Writer) { this.inner = inner; }
write(data: string) {
this.inner.write(data); // manual delegation
}
}
Go
type Writer interface {
Write(p []byte) (n int, err error)
}
type CountingWriter struct {
io.Writer // embed the interface itself!
count int
}
func (cw *CountingWriter) Write(p []byte) (int, error) {
n, err := cw.Writer.Write(p) // delegate to embedded
cw.count += n
return n, err
}
// CountingWriter satisfies Writer automatically
package main
import "fmt"
type Animal struct {
Name string
}
func (a Animal) Speak() string {
return a.Name + " makes a sound"
}
func (a Animal) Describe() string {
return "Animal: " + a.Name
}
type Dog struct {
Animal // embedding promotes Name, Speak, Describe
Breed string
}
// Override Speak — Describe is still promoted from Animal
func (d Dog) Speak() string {
return d.Name + " barks"
}
type Cat struct {
Animal
}
// Cat does not override Speak — uses promoted version
func main() {
d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
c := Cat{Animal: Animal{Name: "Whiskers"}}
fmt.Println(d.Speak()) // Dog override
fmt.Println(d.Animal.Speak()) // explicit base call
fmt.Println(d.Describe()) // promoted from Animal
fmt.Println(d.Name) // promoted field
fmt.Println(d.Breed) // Dog's own field
fmt.Println(c.Speak()) // promoted from Animal
}
How do you embed a type in a Go struct?
What does "method promotion" mean in Go embedding?
Which Go feature provides polymorphism — calling the same method on different types?
Two embedded types both have a method named Save(). What happens when you call outerStruct.Save()?