Skip to content

Type aliases

A type alias introduces a new name for an existing type. It is not a new type; AccountId and String are interchangeable at every level.

type AccountId = String
type Price = Decimal
type Pair = #(Int, Int)

Aliases are transparent: the compiler treats AccountId and String as the same type. A function that takes String accepts an AccountId, and vice versa.

fn greet_by_id(id: AccountId): String { "hello, ${id}" }
let s: String = "005000000000001"
debug greet_by_id(s) // OK: AccountId is just String

If you want a type that is incompatible with its underlying type at the API boundary, use an opaque type instead. Opaque types are nominal: the compiler keeps them distinct.

  • Naming intent at the call site. Id, AccountId, CaseId all alias String, but the name at the call site tells the reader what the caller expects.

  • Shortening long generic types.

    type DmlResult<T> = Result<T, DmlError>
    fn insert(acc: Account): DmlResult<Id> { ... }
  • Giving function types a name. For function-valued parameters and fields, a function type alias reads better than the inline form. See function type aliases for the specific syntax.

type AccountId = String is fine. type Box = List (partial application) is not; aliases do not carry type parameters unless the alias itself is parameterized:

type Box<T> = List<T> // OK: both sides take one parameter
fn empty<T>(): Box<T> { [] }

Aliases have no representation in generated Apex. They are resolved during type-checking and disappear in the emitted code; AccountId in Vertex becomes String in Apex, not a wrapper class.