skip to content

A teammate writes `actual typealias Instant = java.time.Instant` to satisfy `expect class Instant`, and it fails to compile. Walk through the likely causes and how to diagnose them.

level: middleimportance: nice to knowfreq 22%

answer

  1. alias adds no code → type must already match
  2. constructor in expect, static factory on JDK → fails
  3. signature/visibility/generics must align
  4. read the actualization error, it names the member
  5. fix = trim expect or use actual class

basics

~20 s

Usually the expect class declares a member (method, constructor, or companion factory) that java.time.Instant doesn't expose with a matching signature, or visibility/type parameters don't line up. The fix is to align the expect surface or use an actual class.

solid answer

~40 s

An `actual typealias` only compiles when the aliased type already satisfies every member of the `expect`. Common failures: (1) the `expect class` declares a constructor or factory `java.time.Instant` lacks (e.g. an `expect constructor(Long)` — `Instant` uses static `ofEpochMilli` instead, not a constructor); (2) a method signature mismatch (`suspend`, nullability, parameter/return types); (3) the `expect` declares a `companion object` with members that don't map to `Instant`'s statics; (4) visibility too narrow on the platform side; (5) the `expect` and alias generics don't align. Diagnose by reading the compiler's actualization error, which names the missing/mismatched member, then either trim the `expect` to what `Instant` actually provides, or switch to an `actual class Instant` that wraps `java.time.Instant` and implements the missing pieces (e.g. a secondary constructor delegating to `ofEpochMilli`).

go deeper

for a junior

Knows the alias fails if the platform type doesn't match and would read the error.

for a middle

Identifies the specific causes (constructor, signature, companion, visibility, generics) and the trim-or-wrap fixes.

for a senior

Predicts the constructor/static-factory trap and redesigns the expect surface (top-level factories) to keep the alias.

for a principal

Establishes conventions (avoid constructors in expect for JDK value types) to keep aliases robust across the codebase.

## Why an alias can fail Remember: `actual typealias` adds **no code**. So the aliased type must *already* satisfy the `expect` contract member-for-member. If it doesn't, the compiler raises an **actualization error** pointing at the offending member. ## The usual culprits ### 1. Missing constructor ```kotlin // commonMain expect class Instant(epochMillis: Long) // declares a constructor ``` `java.time.Instant` has **no public constructor**; you create it via the static `Instant.ofEpochMilli(...)`. A typealias can't add a constructor, so this fails. Fixes: drop the constructor from the `expect` and add a common factory, or use an `actual class`. ### 2. Signature mismatch The expect's method differs in return type, parameter types, nullability, or the `suspend` modifier from what the platform type offers. Signatures must be compatible. ### 3. Companion / statics mismatch ```kotlin expect class Instant { companion object { fun now(): Instant } } ``` This maps fine because `java.time.Instant.now()` is a static — Kotlin can match it to the companion. But if the `expect` companion declared a member with no static counterpart, it fails. ### 4. Visibility too narrow The actual member's visibility must be at least as permissive as the expected one — you can't satisfy a `public` expect with something effectively less visible. ### 5. Generic parameter mismatch The count and bounds of type parameters on the `expect class` and the aliased type must align. ## Diagnosing - **Read the error**: the Kotlin compiler names the specific member that's missing or mismatched ("actual ... has no corresponding ..."). - **Compare surfaces**: list the `expect` members and check each against the platform type's public API. - **Decide the fix**: - Trim the `expect` to exactly what the platform type provides, or - Switch to `actual class` and implement the gap: ```kotlin // jvmMain actual class Instant actual constructor(epochMillis: Long) { private val backing = java.time.Instant.ofEpochMilli(epochMillis) actual fun toEpochMilli(): Long = backing.toEpochMilli() } ``` ## Key takeaway The alias is only valid for a *clean superset* match. The constructor case is the most common trap because many JDK value types use static factories rather than public constructors.

  • Why is declaring a constructor in the `expect` a frequent cause of alias failure?
    Many JDK value types (Instant, UUID via randomUUID) expose static factories, not public constructors, so the aliased type can't satisfy an expected constructor.
  • How can you keep the alias and still offer a `(Long)` creation API?
    Don't declare a constructor in the expect; instead expose an `expect fun instantOfMillis(ms: Long): Instant` top-level factory that each target implements.

saying these in an interview costs you the question

  • Assuming any JDK type can be aliased regardless of the expect surface
  • Declaring constructors in expect without checking the platform type has them
  • Ignoring the compiler's named actualization error
  • Thinking the alias can add the missing member
  • Forgetting signature/visibility/generic matching rules

context