skip to content

Function-Type & Generic Aliases

Aliasing a function type, like typealias Handler = (Event) -> Unit, turns unreadable signatures into named concepts, and aliases can be parameterized as well. It is a small feature that noticeably improves callback-heavy APIs.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What is a function-type alias in Kotlin, and how would you declare an alias named Handler for a function that takes an Event and returns Unit?

level: juniorimportance: must knowfreq 55%

answer

  1. typealias = nickname, not a new type
  2. (Event) -> Unit is the function type
  3. top-level only, no runtime object
  4. compiler expands alias back to the real type
  5. interchangeable with the raw arrow type

basics

~10 s

It gives a short, readable name to a function type. You write typealias Handler = (Event) -> Unit. Then you can use Handler anywhere instead of writing the long (Event) -> Unit signature.

solid answer

~40 s

A function-type alias uses the `typealias` keyword to name a function type so the long lambda signature reads clearly at use sites. Declaration: `typealias Handler = (Event) -> Unit`. After that, parameters, properties and return types can be typed as `Handler` instead of `(Event) -> Unit`. It is purely a compile-time, top-level naming convenience: the compiler expands `Handler` back to `(Event) -> Unit`, so a `Handler` and a raw `(Event) -> Unit` are the same type and interchangeable, with no runtime object or wrapper created. Aliases must be declared at file/top level (not inside a function or class body). They are great for callback signatures, comparators, and event handlers where the bare arrow syntax would otherwise repeat and obscure intent.

code

kotlin · 11 lines
kotlin
typealias Handler = (Event) -> Unit

class EventBus {
    private val handlers = mutableListOf<Handler>()
    fun subscribe(handler: Handler) { handlers += handler }
    fun publish(event: Event) { handlers.forEach { it(event) } }
}

// A raw (Event) -> Unit is accepted because it IS the same type:
val raw: (Event) -> Unit = { e -> println(e) }
EventBus().subscribe(raw)

go deeper

for a junior

Can write typealias Handler = (Event) -> Unit and use it as a parameter type.

for a middle

Knows it is a compile-time alias interchangeable with the raw function type and has no runtime cost.

for a senior

Explains the no-extra-type-safety consequence and contrasts with fun interface / value class for nominal types.

for a principal

Frames aliases as API-readability tooling and decides when nominal typing (real type) is warranted over an alias in a public contract.

## What a function-type alias is A **type alias** is an alternative name for an existing type, introduced with the `typealias` keyword. A **function-type alias** specifically names a *function type* — a type written with the arrow syntax `(Params) -> ReturnType`. Kotlin function types look like `(Event) -> Unit`, `(Int, Int) -> Int`, or `suspend () -> String`. When these signatures appear repeatedly (callbacks, listeners, comparators) they become noisy. A typealias gives them a domain name: ```kotlin typealias Handler = (Event) -> Unit fun register(onEvent: Handler) { /* ... */ } val h: Handler = { event -> println(event) } register(h) register { event -> println(event) } // lambda literal works too ``` ## Key properties - **`typealias` is top-level only.** It must be declared at file scope (or inside an object/companion as a member in newer versions), *never* inside a function body. - **It is a pure compile-time alias, not a new type.** The compiler erases `Handler` to `(Event) -> Unit`. So `Handler` and `(Event) -> Unit` are the *same* type; you can pass one where the other is expected with no conversion. This is unlike an `inline value class` wrapper, which *is* a distinct type. - **No runtime cost / no wrapper object.** Nothing is allocated; there is no `Handler` class in the bytecode. - **Receiver and suspend forms are supported.** You can alias `typealias Click = View.() -> Unit` (function type *with receiver*) or `typealias AsyncOp = suspend () -> Result`. ## Why use them - Readability at call sites and in public APIs. - A single place to change a callback signature. - Self-documenting names (`Validator`, `Reducer`, `Comparator`-like types). ## Limitations Because it is not a real type, a function-type alias gives **no extra type safety** — you cannot distinguish two aliases of the *same* underlying function type, and you cannot overload solely by alias name. For nominal distinctness you need a real class or a functional interface (`fun interface`).

  • Can you declare a typealias inside a function body?
    No. Type aliases must be declared at the top (file/class) level; declaring one inside a function is a compile error.
  • Does Handler create a new runtime type you can check with `is Handler`?
    No. It expands to `(Event) -> Unit` at compile time, so `is Handler` is just `is Function1<Event, Unit>`; there is no distinct Handler class.

Like a contact nickname in your phone: 'Mom' and the real phone number are the same number, just easier to read.

saying these in an interview costs you the question

  • Saying typealias creates a brand-new distinct type with its own runtime class
  • Claiming it adds type safety so two aliases of the same type can't be mixed
  • Thinking a typealias allocates a wrapper object at runtime
  • Trying to declare the alias inside a method body
  • Confusing typealias with a `fun interface` or `value class`

context

open as a page

How do you declare a parameterized (generic) function-type alias such as a Predicate, and what is its relationship to the underlying function type?

level: middleimportance: must knowfreq 45%

basics

~10 s

Add type parameters in angle brackets: typealias Predicate<T> = (T) -> Boolean. Then Predicate<String> means (String) -> Boolean. The compiler just substitutes the type argument and expands the alias.

open as a page

Can a function-type alias capture a receiver type or a suspend modifier? Show how you'd alias a DSL builder lambda and an async operation.

level: seniorimportance: should knowfreq 30%

basics

~20 s

Yes. You can alias a function type with a receiver, like typealias HtmlBlock = Html.() -> Unit, and a suspending one, like typealias AsyncOp = suspend () -> Result. The modifiers are part of the function type the alias names.

open as a page

When should you prefer a function-type alias over a `fun interface` (SAM) for a callback, and what are the trade-offs?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Use a typealias when you only want a readable name for a lambda signature with no extra behavior. Use a fun interface when you need a real distinct type, default methods, or nominal type safety. The alias is just a name; the interface is a true type.

open as a page

What are the practical pitfalls of relying on function-type and generic aliases — error messages, overload conflicts, and visibility — in a shared codebase?

level: principalimportance: nice to knowfreq 20%

basics

~20 s

Because aliases are just names that expand away, they don't add safety: same-typed aliases clash on overloads, error messages may show the raw function type instead of your name, Java can't see them, and an alias can't be more public than the type it points to.

open as a page