Interfaces & Implicit Satisfaction
Interfaces: structural but implicit
Section titled “Interfaces: structural but implicit”TypeScript uses structural typing — if a type has the right shape, it’s compatible. Go interfaces work the same way, but with one key difference: you never write implements. If a type has all the methods an interface requires, it satisfies that interface automatically. This is called implicit satisfaction.
Defining and satisfying interfaces
Section titled “Defining and satisfying interfaces”interface Greeter { greet(): string;}
// TypeScript: no implements needed (structural)class User { constructor(public name: string) {} greet(): string { return `Hello from ${this.name}`; }}
function sayHello(g: Greeter) { console.log(g.greet());}type Greeter interface { Greet() string}
type User struct { Name string }
// No "implements" — User satisfies Greeter automaticallyfunc (u User) Greet() string { return "Hello from " + u.Name}
func sayHello(g Greeter) { fmt.Println(g.Greet())}The power: decouple definition from implementation
Section titled “The power: decouple definition from implementation”Because satisfaction is implicit, the interface can be defined in the package that uses it, not in the package that provides the implementation. This means a library author doesn’t need to know about your interface — if their type has the right methods, it works.
// TypeScript — interface often lives with the classinterface Logger { log(msg: string): void;}class ConsoleLogger implements Logger { log(msg: string) { console.log(msg); }}// Go — interface lives with the consumer// (in the package that needs it, not the one that provides it)type Logger interface { Log(msg string)}
// This type in another package doesn't know about Loggertype ConsoleLogger struct{}func (c ConsoleLogger) Log(msg string) { fmt.Println(msg) }// ConsoleLogger satisfies Logger without knowing Logger existsTry it
Section titled “Try it”package main
import ( "fmt" "math")
type Shape interface { Area() float64 Perimeter() float64}
type Circle struct{ Radius float64 }type Rect struct{ Width, Height float64 }
func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius }func (c Circle) Perimeter() float64 { return 2 * math.Pi * c.Radius }
func (r Rect) Area() float64 { return r.Width * r.Height }func (r Rect) Perimeter() float64 { return 2 * (r.Width + r.Height) }
func describe(s Shape) { fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())}
func main() { shapes := []Shape{ Circle{Radius: 5}, Rect{Width: 4, Height: 3}, } for _, s := range shapes { describe(s) }}Loading Go runtime (first run only, ~8 MB)…