Skip to content

Interfaces & Implicit Satisfaction

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.

TypeScript
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());
}
Go
type Greeter interface {
Greet() string
}
type User struct { Name string }
// No "implements" — User satisfies Greeter automatically
func (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
// TypeScript — interface often lives with the class
interface Logger {
log(msg: string): void;
}
class ConsoleLogger implements Logger {
log(msg: string) { console.log(msg); }
}
Go
// 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 Logger
type ConsoleLogger struct{}
func (c ConsoleLogger) Log(msg string) { fmt.Println(msg) }
// ConsoleLogger satisfies Logger without knowing Logger exists
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)
}
}
How does a Go type signal it implements an interface?
Where is the idiomatic place to define a Go interface?
TypeScript structural typing and Go implicit interface satisfaction are:
What is the empty interface in Go (a type that every type satisfies)?