Anonymous Interface Intersections in Go

Categories: Programming

Interface intersections are a quite handy feature in Typescript. For those without Typescript experience, they allow you to easily define a new type that is the intersection of two different types. To pull an example from the language guide, say you have an interface for objects with a colour and an interface for circles, like so:

interface Colourful {
  colour: string;
}
interface Circle {
  radius: number;
}

Now a lot of your logic may deal with only a Colourful object or only a Circle, but sometimes you want to write something that operates only on a circle that is also colourful. In Typescript you can easily combine these two interfaces into a new type definition:

type ColourfulCircle = Colourful & Circle;

and anything that is both a Colourful and a Circle will be usable as a ColourfulCircle. That’s great, but what is even nicer is that you don’t even have to define a type for this. If there’s not any real reason for you to make a named type, you can just make an anonymous type definition inline like so:

function doSomething(c: Colourful & Circle) {
    console.log(`The circle has radius ${c.radius} and colour ${c.colour}`);
}

While it isn’t often used, Go actually has this same capability. Lets make our original two interfaces in Go-form first:

type Colourful interface {
	Colour() string
}

type Circle interface {
	Radius() int 
}

We had to switch to methods because Go interfaces can’t describe struct fields like how TS interfaces can describe object properties, but this is broadly the same thing. Most Gophers probably know that we can then define

type ColourfulCircle interface {
	Colourful
	Circle
}

to create a named type for Circles that are also Colourful. This is a little more verbose than Typescript’s equivalent, but Go’s always been a language that values directness and linguistic simplicity over brevity, so that’s not unexpected. What is a lot less commonly known is that you can create anonymous interface definitions inline just like you can create anonymous struct definitions, so that means if you want to duplicate the anonymous inline type intersection we had in Typescript, you can just do:

func doSomething(cc interface {
    Colourful
    Circle
}) {
    fmt.Printf("The circle has radius %d and colour %s", cc.Radius(), cc.Colour())
}

and it will compile just fine.

With that said, should you do this? Eh, it’s not a common pattern in Go, which means that you probably should avoid it in the interest of making sure your code is easily understood. If you’re using a given intersection more than once, this will also add a lot of lines to your source files, which also isn’t great for comprehension. That said, while I don’t think this is usually a good idea to use in committed files, it can be quite handy as a temporary measure when you’re not sure what interfaces you’ll need just yet.