How do you write generic and receiver-based function-type typealiases, e.g. `Validator<T>` or an alias for `A.() -> B`? Show the syntax and how generics flow through.
answer
- typealias Name<T> = (T) -> R
- Receiver: typealias Block<T> = T.() -> Unit
- suspend/nullable aliases allowed
- ((E)->Unit)? vs (E)->Unit? — parentheses matter
- No in/out variance on the alias itself
basics
~20 sAdd type parameters after the alias name: typealias Validator<T> = (T) -> Boolean. The T is filled in when you use it, e.g. Validator<String>. You can also alias receiver function types like typealias Block<T> = T.() -> Unit.
solid answer
~40 sA typealias may declare its own type parameters in angle brackets, which the right-hand side references: `typealias Validator<T> = (T) -> Boolean`, used as `Validator<String>`. You can supply defaults (`typealias Producer<T = Unit> = () -> T`) and alias **receiver** function types: `typealias Builder<T> = T.() -> Unit`, where `T` becomes the receiver so the lambda body can call `T`'s members via `this`. You can also alias **suspend** and **nullable** function types: `typealias Async<T> = suspend () -> T`, `typealias OptHandler = ((Event) -> Unit)?`. Type-parameter variance keywords are not allowed on a typealias declaration itself. Because the alias is transparent, `Validator<String>` *is* `(String)->Boolean`, so generic substitution is plain textual/structural expansion — no new type is created. Aliases are convenient for DSL builder signatures where receiver types make call sites readable.
code
kotlin · 8 linestypealias Reducer<S, A> = (S, A) -> S
typealias Block<T> = T.() -> Unit
typealias Async<T> = suspend () -> T
fun <T> build(seed: T, block: Block<T>): T { seed.block(); return seed }
val count: Reducer<Int, Int> = { acc, x -> acc + x }
val sb = build(StringBuilder()) { append("x").append("y") } // this == StringBuildergo deeper
Can add a single type parameter and use the alias with a concrete type.
Writes multi-param, receiver, suspend, and nullable aliases correctly, including the parentheses subtlety.
Knows variance restrictions, default type params, and that substitution is transparent structural expansion.
Uses generic/receiver aliases to shape ergonomic DSL and functional-pipeline APIs and reasons about their evolution and limits.
## Generic function-type typealiases Declare type parameters right after the name; the right-hand side uses them. ```kotlin typealias Validator<T> = (T) -> Boolean typealias Mapper<A, B> = (A) -> B typealias Reducer<S, A> = (S, A) -> S val nonEmpty: Validator<String> = { it.isNotEmpty() } val len: Mapper<String, Int> = { it.length } ``` When you write `Validator<String>`, the compiler substitutes `T = String`, yielding `(String) -> Boolean`. Because a typealias is **transparent** (erased before type checking), this is plain structural expansion — `Validator<String>` *is* `(String) -> Boolean`, not a distinct type. ## Defaults for type parameters ```kotlin typealias Producer<T = Unit> = () -> T val p: Producer<Int> = { 42 } val u: Producer<> = { } // not valid syntax; use Producer for the default? -> use explicit ``` Defaults are allowed (`typealias Producer<T = Unit> = () -> T`) and resolve when the argument is omitted in compatible positions. ## Receiver function types: `A.() -> B` A **receiver function type** `T.() -> R` is a function that runs with a `T` as its implicit `this` (the receiver). Aliasing one makes builder/DSL signatures readable: ```kotlin typealias Block<T> = T.() -> Unit fun <T> configure(target: T, block: Block<T>): T { target.block() // calls the lambda with target as receiver return target } configure(StringBuilder()) { append("hi") } // 'this' is the StringBuilder ``` Here `T` is both a type parameter of the alias *and* the receiver of the function type. ## suspend and nullable variants ```kotlin typealias Async<T> = suspend () -> T // suspend function type typealias OptHandler = ((Event) -> Unit)? // nullable function type — note the parentheses ``` The parentheses in `OptHandler` matter: `(Event) -> Unit?` would mean a function returning `Unit?`, whereas `((Event) -> Unit)?` is a *nullable function*. ## Restrictions - **No variance keywords** (`in`/`out`) on the typealias's own type parameters — declare variance on the underlying class/interface instead. - **Top-level only** — no nesting in classes/functions. - The alias adds no constraints the underlying type doesn't have; e.g. you can't add `where T : Comparable<T>` bounds to enforce something the function type wouldn't. ## Why it helps Generic aliases turn noisy higher-order signatures (`(S, A) -> S`, `T.() -> Unit`) into intent-revealing names (`Reducer<S, A>`, `Block<T>`), which is especially valuable in DSLs and functional pipelines.
- What's the difference between `((Event) -> Unit)?` and `(Event) -> Unit?` in an alias?The first is a nullable function; the second is a non-null function whose return type is the nullable `Unit?`. Parentheses change the meaning.
- Can you put `out`/`in` variance on a typealias type parameter?No. Variance must be declared on the underlying class/interface; typealias type parameters can't carry variance modifiers.
saying these in an interview costs you the question
- Putting in/out variance on the typealias parameters
- Confusing ((E)->Unit)? with (E)->Unit?
- Thinking Validator<String> is a distinct type from (String)->Boolean
- Not knowing receiver function types can be aliased
- Believing aliases can add generic bounds the underlying type lacks