As someone who has worked primarily with Typescript over the last few years, learning Golang was challenging as much as it was stimulating. Recap: What are interfaces? Interfaces ensure consistent behavior without focusing on implementation details. Interfaces in TypeScript are structural TypeScript is a structurally typed type system, one way it achieves this is using interfaces. TypeScript is a structurally typed type system, one way it achieves this is using interfaces. TypeScript is a structurally typed type system, one way it achieves this is using interfaces. structurally typed They allow you to define the structure of objects you expect to handle within your code. Let’s take a common example of a Person object. Person interface Person { name: string talk(text: string): void walk(to: string): string } const john: Person = { name: 'John Doe', talk: function (text: string): void { console.log('Speaking: ', text) }, walk: function (to: string): string { return `${this.name} is at ${to}` }, } interface Person { name: string talk(text: string): void walk(to: string): string } const john: Person = { name: 'John Doe', talk: function (text: string): void { console.log('Speaking: ', text) }, walk: function (to: string): string { return `${this.name} is at ${to}` }, } Go’s interfaces are behavioral Go uses duck typing, meaning a struct matches an interface as long as it has matching method signatures. duck typing In Go, there is no explicit implements keyword, all structs implicitly implement interfaces. The compiler deduces this during build time. no explicit implements keyword // interface to be implemented by the Person struct type PersonLike interface { Walk(string) string Talk(string) } // a `Person` struct type Person struct { Name string } // Talk implements PersonBehavior. func (p Person) Talk(text string) { println(p.Name + "is saying: '" + text) } // Walk implements PersonBehavior. func (p Person) Walk(text string) string { return p.Name + "is at " + text } func main() { // declare `john` of PersonLike type var john PersonLike // assign an instance of the Person john = Person{} // invoke `.Walk` on the object println(john.Walk("home")) } // interface to be implemented by the Person struct type PersonLike interface { Walk(string) string Talk(string) } // a `Person` struct type Person struct { Name string } // Talk implements PersonBehavior. func (p Person) Talk(text string) { println(p.Name + "is saying: '" + text) } // Walk implements PersonBehavior. func (p Person) Walk(text string) string { return p.Name + "is at " + text } func main() { // declare `john` of PersonLike type var john PersonLike // assign an instance of the Person john = Person{} // invoke `.Walk` on the object println(john.Walk("home")) } In the above example, a struct is first created and then the methods are implemented on it. The compiler automatically detects the implementation of the interface by the Person struct. Any struct having the method signature will satisfy the interface type The compiler automatically detects the implementation of the interface by the Person struct. Any struct having the method signature will satisfy the interface type Person The example below demonstrates how the same object instance can satisfy multiple interfaces, each containing a subset of its method signatures. package main // interface to be implemented by the Person struct type PersonLike interface { Walk(string) string Talk(string) } // interface that only has `Walk` type Walker interface { Walk(string) string } // interface that only has `Talk` type Talker interface { Talk(string) } // a `Person` struct type Person struct { Name string } func (p Person) Talk(text string) { println(p.Name + "is saying: '" + text) } func (p Person) Walk(text string) string { return p.Name + "is at " + text } func main() { // declare `john` of PersonLike type var john PersonLike // assign an instance of the Person john = Person{} var johnWalker Walker // assign to object to `Walker` type johnWalker = john // invoke `.Walk` johnWalker.Walk("home") // Error below: johnWalker.Talk is undefined (Walker does not have Talk method) johnWalker.Talk("I am home") var johnTalker Talker johnTalker = john johnTalker.Talk("How do I get home?") // invoke `.Walk` on the object println(john.Walk("home")) // "John is at home" println(john.(Person).Name) // John } package main // interface to be implemented by the Person struct type PersonLike interface { Walk(string) string Talk(string) } // interface that only has `Walk` type Walker interface { Walk(string) string } // interface that only has `Talk` type Talker interface { Talk(string) } // a `Person` struct type Person struct { Name string } func (p Person) Talk(text string) { println(p.Name + "is saying: '" + text) } func (p Person) Walk(text string) string { return p.Name + "is at " + text } func main() { // declare `john` of PersonLike type var john PersonLike // assign an instance of the Person john = Person{} var johnWalker Walker // assign to object to `Walker` type johnWalker = john // invoke `.Walk` johnWalker.Walk("home") // Error below: johnWalker.Talk is undefined (Walker does not have Talk method) johnWalker.Talk("I am home") var johnTalker Talker johnTalker = john johnTalker.Talk("How do I get home?") // invoke `.Walk` on the object println(john.Walk("home")) // "John is at home" println(john.(Person).Name) // John } Empty interfaces Go allows you to create interfaces that have no methods declared i.e., it can hold a value of any type without any restrictions. Go type Empty interface{} type Empty interface{} The TypeScript equivalent would be the unknown type TypeScript unknown const empty: unknown = {} const empty: unknown = {} Empty interfaces come in handy when the programmer knows the type but it is unknown to the type system. A common example would be de-serializing unknown types. func parse(val interface{}) (string, error) { // Gets the type of `value` switch value := val.(type) { case string: return string(value), nil default: return "", fmt.Errorf("unsupported data %#v", value) } } func parse(val interface{}) (string, error) { // Gets the type of `value` switch value := val.(type) { case string: return string(value), nil default: return "", fmt.Errorf("unsupported data %#v", value) } } Common Gotcha: Interfaces on pointer types Common Gotcha: Interfaces on pointer types Methods in Go can have either Pointer receivers or value receivers. Depending on what is used the compiler treats them differently while matching interfaces. The following code won’t be compiled because of the error in line 39. package main // interface that only has `Talk` type Talker interface { Talk(string) } // a `PersonPtr` struct that has a pointer receiver for `Talk` type PersonPtr struct { Name string } // Add implements PersonBehavior. func (p *PersonPtr) Talk(text string) { println(p.Name + "is saying: '" + text) } // a `Person` struct that has a value receiver for `Talk` type Person struct { Name string } // Add implements PersonBehavior. func (p Person) Talk(text string) { println(p.Name + "is saying: '" + text) } func main() { // assign an instance of the Person var john Talker johnny := PersonPtr{} johnny.Talk("I can talk") // Error: PersonPtr does not implement Talker (method Talk has pointer receiver) john = johnny john.Talk("This wont work") } package main // interface that only has `Talk` type Talker interface { Talk(string) } // a `PersonPtr` struct that has a pointer receiver for `Talk` type PersonPtr struct { Name string } // Add implements PersonBehavior. func (p *PersonPtr) Talk(text string) { println(p.Name + "is saying: '" + text) } // a `Person` struct that has a value receiver for `Talk` type Person struct { Name string } // Add implements PersonBehavior. func (p Person) Talk(text string) { println(p.Name + "is saying: '" + text) } func main() { // assign an instance of the Person var john Talker johnny := PersonPtr{} johnny.Talk("I can talk") // Error: PersonPtr does not implement Talker (method Talk has pointer receiver) john = johnny john.Talk("This wont work") } This can however be fixed by replacing the assignment that line 35 with: line 35 john = &johnny john = &johnny Why does this work? Since Talk is implemented only for *PersonPtr, passing a pointer (&johnny) to Talker, ensuring it satisfies the interface. Since Talk is implemented only for *PersonPtr, passing a pointer (&johnny) to Talker, ensuring it satisfies the interface. Talk *PersonPtr &johnny Talker Conclusion Go interfaces enable implicit implementation, reducing boilerplate and improving flexibility. Duck typing allows structs to satisfy interfaces automatically if method signatures match. Multiple interface satisfaction lets a struct conform to different behaviors dynamically. Empty interfaces (interface{}) provide a way to handle unknown types, useful for generics. Pointer vs. value receivers impact interface satisfaction - choosing the right one is key. Go interfaces enable implicit implementation, reducing boilerplate and improving flexibility. Go interfaces enable implicit implementation Duck typing allows structs to satisfy interfaces automatically if method signatures match. Duck typing Multiple interface satisfaction lets a struct conform to different behaviors dynamically. Multiple interface satisfaction Empty interfaces (interface{}) provide a way to handle unknown types, useful for generics. Empty interfaces ( interface{} ) Pointer vs. value receivers impact interface satisfaction - choosing the right one is key. Pointer vs. value receivers impact interface satisfaction Further Reading https://www.alexedwards.net/blog/interfaces-explained https://gobyexample.com/interfaces https://www.alexedwards.net/blog/interfaces-explained https://www.alexedwards.net/blog/interfaces-explained https://gobyexample.com/interfaces https://gobyexample.com/interfaces Hope you enjoyed reading this as much as I enjoyed writing it.If you think this will be of help to someone? Do not hesitate to share. If you liked it, tap the clap below so other people will see this here on Medium. Don’t forget to show some love by following the blog! Hope you enjoyed reading this as much as I enjoyed writing it. If you think this will be of help to someone? Do not hesitate to share. If you liked it, tap the clap below so other people will see this here on Medium. Don’t forget to show some love by following the blog! clap following the blog!