Go functions with multiple types: `any`, type switches, and generics

Golang function parameter multiple types: use any/interface{} with a type switch, golang function types with type parameters (generics), go function types and golang func type constraints, plus golang pass type as argument patterns and links to interfaces and functions in Go.

Published

Updated

Read time 4 min read

Reviewed byDeepak Prasad

Go functions with multiple types: `any`, type switches, and generics

A function parameter has a static type, but you can still accept golang multiple types at call sites in two mainstream ways: any (empty interface) with a type switch, or generics with a type parameter and a constraint (a golang function type in the sense of func types is different—that is a callable signature used as a value type; see functions in Go). Queries like golang function types, go function types, golang type function, go type function, golang func type, golang function parameter multiple types, and golang pass type as argument usually boil down to one of those patterns. This article compares them with small programs; for variables and interfaces background, follow those guides.

Tested with Go 1.24 on Linux.


any / interface{}: one parameter, many concrete types

If every caller passes a different concrete type but you only need to inspect or dispatch, take any (alias for interface{}) and use a type switch or type assertion.

Route []string and map[string]string through one helper

go
package main

import "fmt"

func A(i []string) { C(i) }
func B(i map[string]string) { C(i) }

func C(i any) {
	fmt.Printf("the type was %T\n", i)
}

func main() {
	A([]string{"a"})
	B(map[string]string{"k": "v"})
}
Output

This prints the dynamic type for each call.

string, int, and float64 with a type switch

go
package main

import "fmt"

func describe(x any) string {
	switch x.(type) {
	case string:
		return "This is a string"
	case int:
		return "This is an int"
	case float64:
		return "This is a float64"
	default:
		return "default value"
	}
}

func main() {
	fmt.Println(describe(12))
	fmt.Println(describe("abc"))
	fmt.Println(describe(11.04))
}
Output

Running it prints the three describe lines ending with the int, string, and float64 branches.


Generics: one implementation, int | float64 (and beyond)

Generics (since Go 1.18) let you write a single function whose type parameter V is constrained to a union of types. That is the idiomatic fix when the same algorithm applies to each member of the union—here, summing a slice.

Non-generic duplication for comparison:

go
package main

import "fmt"

func SumInts(m []int) int {
	s := 0
	for _, v := range m {
		s += v
	}
	return s
}

func SumFloats(m []float64) float64 {
	var s float64
	for _, v := range m {
		s += v
	}
	return s
}

func main() {
	ints := []int{10, 2, 85, 41, 5}
	floats := []float64{10.2, 21.5, 5.6, 40.5, 6.5}
	fmt.Printf("Non-generic sums: %v and %v\n", SumInts(ints), SumFloats(floats))
}

One generic replacement:

go
package main

import "fmt"

func SumIntsOrFloats[V int | float64](m []V) V {
	var s V
	for _, v := range m {
		s += v
	}
	return s
}

func main() {
	ints := []int{10, 2, 85, 41, 5}
	floats := []float64{10.2, 21.5, 5.6, 40.5, 6.5}
	fmt.Printf("Generic sums: %v and %v\n",
		SumIntsOrFloats(ints),
		SumIntsOrFloats(floats))
}
Output

That prints Generic sums: 143 and 84.3 when run locally.

The bracket list [V int | float64] introduces type parameter V allowed to be int or float64; the compiler infers V at each call—this is the usual answer when people search golang pass type as argument but really want compile-time polymorphism.


Choosing a pattern

  • any plus a type switch — few branches, unrelated shapes, or a true bag of values.
  • Named interface{ ... } — all inputs share behavior you can express as methods (see interfaces).
  • Generics — the same code path for several concrete types with shared structure (int | float64, constraints.Ordered, or your own interface constraint).

Summary

Golang function parameter multiple types does not mean listing string | int in a single parameter slot the way some languages allow union parameters; instead you model golang multiple types with any and runtime dispatch, with a proper interface, or with generic type parameters. Go function types and golang func type usually refer to function values (func(int) bool), not multiple argument families—keep that vocabulary separate. Golang type function searches often land on generics or reflect, not on ordinary signatures. Pick any when behavior diverges by type; pick generics when the algorithm is identical for each allowed type.


References


Frequently Asked Questions

1. How do I give a golang function parameter multiple types?

Either take any (formerly interface{}) and branch with a type switch, take a real interface type whose methods all arguments satisfy, or use generics with a type parameter constrained to a union like int | float64 or constraints.Integer.

2. What is a golang function type in this context?

It is a signature used as a type, such as func(int) bool; unrelated to accepting multiple argument types unless you mean a generic function func F[T, U](t T, u U).

3. When should I use generics instead of any?

Prefer generics when several concrete types share structure (same algorithm on int and float64 slices); prefer any plus a type switch when shapes differ a lot or you only need a few cases.

4. Can I golang pass type as argument like in some languages?

Go has no runtime “type argument” parameter; you pass values, use generics for compile-time type parameters, or pass reflect.Type for reflection-heavy APIs.

5. Is empty interface still valid if I see `any` in new code?

any is a predefined alias for interface{}; they mean the same thing.
Tuan Nguyen

Data Scientist

Proficient in Golang, Python, Java, MongoDB, Selenium, Spring Boot, Kubernetes, Scrapy, API development, Docker, Data Scraping, PrimeFaces, Linux, Data Structures, and Data Mining. With expertise …