Interfaces
Structural typing and Go's implicit interface satisfaction
Introduction
In this lesson, you'll learn about interfaces in Go. Coming from JavaScript, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.
In JavaScript, you're familiar with structural typing and go's implicit interface satisfaction.
Go has its own approach to structural typing and go's implicit interface satisfaction, which we'll explore step by step.
The Go Way
Let's see how Go handles this concept. Here's a typical example:
package main
import "fmt"
// Interface declaration
type Speaker interface {
Speak() string
}
type Mover interface {
Move() string
}
// Compose interfaces
type Animal interface {
Speaker
Mover
}
// Types implicitly satisfy interfaces — no "implements" keyword
type Dog struct{ Name string }
func (d Dog) Speak() string { return "Woof" }
func (d Dog) Move() string { return "runs" }
type Robot struct{}
func (r Robot) Speak() string { return "Beep" }
func (r Robot) Move() string { return "rolls" }
func makeNoise(s Speaker) {
fmt.Println(s.Speak())
}
// Empty interface — accepts any type
func printAny(v any) { fmt.Println(v) }
func main() {
makeNoise(Dog{"Rex"}) // Woof
makeNoise(Robot{}) // Beep
var a Animal = Dog{"Rex"} // Dog satisfies Animal
fmt.Println(a.Move())
}Comparing to JavaScript
Here's how you might have written similar code in JavaScript:
// JS uses duck typing — no explicit interface declaration
class Dog {
speak() { return "Woof"; }
move() { return "runs"; }
}
class Robot {
speak() { return "Beep"; }
move() { return "rolls"; }
}
// Any object with speak() works
function makeNoise(animal) {
console.log(animal.speak());
}
makeNoise(new Dog()); // "Woof"
makeNoise(new Robot()); // "Beep"You may be used to different syntax or behavior.
Go interfaces are satisfied implicitly — no 'implements' declaration needed
You may be used to different syntax or behavior.
Interface is satisfied if the type has all the required methods
You may be used to different syntax or behavior.
Interfaces can be composed by embedding other interfaces
You may be used to different syntax or behavior.
'any' (alias for interface{}) accepts any type — like JS's any
You may be used to different syntax or behavior.
Interface values hold (type, value) pairs; nil interface != nil pointer
Step-by-Step Breakdown
1. Implicit Satisfaction
A type satisfies an interface automatically when it has all required methods. No 'implements' keyword needed.
// JS duck typing — just use the objecttype Writer interface { Write([]byte) (int, error) }
type File struct{}
func (f File) Write(b []byte) (int,error) { ... }
// File now implicitly implements Writer2. Interface Composition
Embed interfaces inside other interfaces to compose behaviors. io.ReadWriter = io.Reader + io.Writer.
// Mixins via Object.assign or class extends multipletype ReadWriter interface {
Reader // embeds Reader interface
Writer // embeds Writer interface
}3. Empty Interface (any)
interface{} (or 'any' in Go 1.18+) accepts any type. Use type assertions to recover the concrete type.
function printAny(v) { console.log(v); }func printAny(v any) { fmt.Println(v) }
// Type assertion
if s, ok := v.(string); ok {
fmt.Println("string:", s)
}4. Interface for Testing
Define interfaces for dependencies so you can swap them with mock implementations in tests.
// Jest: jest.fn() replaces the functiontype DB interface { Query(sql string) ([]Row, error) }
// Real: SqlDB{}
// Test: MockDB{} with fake Query()Common Mistakes
When coming from JavaScript, developers often make these mistakes:
- Go interfaces are satisfied implicitly — no 'implements' declaration needed
- Interface is satisfied if the type has all the required methods
- Interfaces can be composed by embedding other interfaces
Key Takeaways
- Interfaces are satisfied implicitly — any type with the right methods qualifies
- Compose interfaces by embedding: type RW interface { Reader; Writer }
- 'any' is the empty interface — use type assertions to recover concrete type
- Design: accept interfaces in parameters, return concrete types