A type alias gives an existing type a clearer name. It does not create a new type.
When to use a type alias
Use a type alias when a type annotation would be repetitive or when a name carries domain meaning.
UserId as Int
HtmlFragment as String
RouteParts as {String}
Aliases are transparent
An alias is another spelling of the same type.
UserId as Int
id UserId = 42
raw Int = id
id has type UserId, but it is also an Int. You can pass it anywhere an Int is expected.
UserId as Int makes annotations clearer, but it does not stop an ordinary Int from being used as UserId.
Alias versus struct
A struct creates a brand new type with its own fields and constructor. An alias does not.
UserId as Int
User = |
id Int,
|
UserId is Int. User is a separate type.
One collection alias
An alias can name a collection type.
Names as {String}
names Names = {"Priya", "Rob"}
A type alias is a compile-time name for an existing type. It introduces no runtime value and no new semantic type identity.
Definition and syntax
UserId as Int
Names as {String}
MaybeName as String?
StringBox as Box of String
The syntax is AliasName as ExistingType. Alias names follow type-name casing. The target may be any valid type expression that resolves to a concrete semantic type.
Exact contract
- A type alias is a compile-time name for an existing type.
- Aliases are transparent: an alias and its target denote the same
TypeId. - Aliases do not create nominal identity.
- Alias and target values are type-compatible in both directions.
- Aliases are valid in type positions such as parameters, fields, returns and local annotations.
- Aliases are not ordinary values. They cannot be assigned, passed, copied or used in expression position.
as in a type-alias declaration is not a cast.
Valid targets
An alias target may be:
- a builtin scalar such as
Int, Float, Bool, Char or String - a struct or choice declared in the same file
- an option such as
String? - a growable collection such as
{String} - a fixed collection such as
{4 Int} - a hashmap such as
{String = Int} - a fully concrete generic instance such as
Box of String - a dependency-bound type, dependency-bound type alias or external package type
Concrete generic aliases are supported. Parameterized aliases and partial generic application are outside the current language scope.
Transparency
UserId as Int
id UserId = 42
raw Int = id
Because UserId is transparent, assigning a UserId value to an Int binding is valid. The reverse is also valid: an Int value can be used where UserId is expected.
Contrast with a struct
UserId as Int
User = |
id Int,
|
UserId is another name for Int. User is a distinct nominal struct type with its own constructor and fields. Use a struct when the domain concept needs its own identity. Use an alias when only the annotation name matters.
Alias versus nominal domain type
An alias improves spelling and readability, but it doesn't isolate a domain value from its target type. UserId as Int still allows an ordinary Int where UserId is expected. Use a wrapper struct today when distinct nominal identity matters.
Moth has no accepted primitive-backed nominal wrapper syntax. Such a wrapper remains an open design question and any future mechanism would stay separate from transparent as aliases.
The accepted alias contract includes external package types. The current compiler still rejects aliases to external opaque types. See @../progress (Progress matrix) for current support.
Empty literals and aliases
An alias can provide the explicit element type needed by an empty collection or map literal.
Names as {String}
names Names = {}
Alias chains
Aliases may chain through other aliases. The chain remains transparent and resolves to the final target type.
Name as String
Names as {Name}
names Names = {"Priya"}
Cycles
Alias cycles are invalid. The compiler reports a circular dependency.
Aliases are not constructors
An alias does not create a new constructor. The alias name is not callable even when the alias targets a constructible nominal type. Construct the target type using its canonical nominal name.
For struct aliases, only the struct's canonical name constructs:
Person = |
name String,
|
MainPerson as Person
person MainPerson = Person("Priya")
For choice aliases, only the choice's canonical name constructs variants:
Status ::
Ready,
Failed | message String |,
;
State as Status
ready State = Status::Ready
failed State = Status::Failed("bad")
Aliases to scalar or otherwise non-constructible targets are not callable for the same reason: the alias name is a type, not a value.
Visibility and module public surfaces
Type aliases follow ordinary module visibility rules. A type alias declared in a source file is dependency-bindable by other files in the same module. Cross-module visibility is controlled by the module root's explicit export: surface. The normal module-root filename itself has no semantic role. A public re-export may expose a dependency-bound alias under a chosen public name. That operation is not the same as an ordinary file-local dependency alias.
Related concepts
- For concrete generic application see Generics.
- For dependency aliasing see the next section.
- For project visibility and module roots see Project Structure and Packages.