In Go, type is a keyword used to define a new named type based on some underlying type (a struct, an existing primitive, an interface, a function signature, etc).
type User struct { // a struct type
Name string
}
type Age int // a named type based on int
type Shape interface { // an interface type
Area() float64 // method with float return
}
type Handler func(w http.ResponseWriter, r *http.Request) // a function type
In short: type is how you tell Go "here's a new named shape of data (or contract) that the rest of my code can refer to by this name."
It's not a variable declaration, it declares a type (a blueprint), not a value. You still need var or := to create an actual value of that type: var u User or u := User{Name: "Jo"}.
type User struct {
Name string
}
// This just declares the shape — no value exists yet:
// type User struct { Name string }
// These actually create values:
var u User
u2 := User{Name: "Jo"}
Struct types are the most common use, grouping related fields together into one named shape (type User struct {...}), like collecting FirstName, Email, and Password into a single User instead of passing them around as separate variables.
type User struct {
FirstName string
Email string
Password string
}
func createUser(u User) {
fmt.Println("Creating user:", u.FirstName, u.Email)
}
// Instead of: createUser(firstName, email, password string)
u := User{FirstName: "Jo", Email: "[email protected]", Password: "secret"}
createUser(u)
Type aliasing over primitives (type Age int) creates a distinct type that the compiler treats as separate from plain int, even though it behaves like one underneath. This is a type-safety tool: it stops you from accidentally passing an int where an Age was expected, catching mistakes the compiler would otherwise let slide.
type Age int
func greet(a Age) {
fmt.Println("You are", a, "years old")
}
func main() {
var a Age = 30
greet(a) // works fine
var i int = 25
greet(i) // compile error: cannot use i (type int) as type Age
}
Interface types (type Shape interface {...}) declare a contract of method signatures rather than any concrete data.
type Shape interface {
Area() float64
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return 3.14159 * c.Radius * c.Radius
}
func printArea(s Shape) {
fmt.Println("Area:", s.Area())
}
// Circle satisfies Shape just by having an Area() method
printArea(Circle{Radius: 5})
Once a type is declared, you can attach methods to it using a receiver: func (u User) SayHi() {}. This works for struct types and named primitive types alike (not for interfaces, methods on interfaces are the required contract itself, not something you attach afterward).