Struct Embedding
TypeScript: class inheritance and mixins
Section titled “TypeScript: class inheritance and mixins”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.
Basic embedding
Section titled “Basic embedding”Declare an embedded type by naming it without a field name inside the struct body.
// TypeScript — class inheritanceclass 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 barksconsole.log(d.name); // Rex — inherited// 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}Method promotion
Section titled “Method promotion”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 — mixin pattern for reusetype Serializable = { serialize(): string;};function withSerialize<T extends object>(Base: new(...a: any[]) => T) { return class extends Base implements Serializable { serialize() { return JSON.stringify(this); } };}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 itfunc (s UserService) CreateUser(name string) { s.Log("creating user: " + name) // ... business logic}Embedding and interfaces
Section titled “Embedding and interfaces”Because promoted methods count toward the outer struct’s method set, embedding is commonly used to satisfy interfaces without boilerplate delegation.
// TypeScript — implement interface manuallyinterface 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 }}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 automaticallyTry it
Section titled “Try it”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 Animalfunc (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}Loading Go runtime (first run only, ~8 MB)…