Extension classes group functions that can be called as members on an existing type.
They are useful when behavior belongs with a type at the call site, but the original type should not be changed.
🔌 StringMethods is extension of string
(
reverseTrim() is string (
)
trimUnicode() is string (
)
)
An extension class is not a class in the nominal type sense. It does not create values, does not participate in inheritance, and does not add nominal weight to the type it extends. It is a transparent grouping construct for extension members.
Targets
An extension class can target primitives, reference classes, and primitive classes.
🔌 StringMethods is extension of string (
)
🔌 UserMethods is extension of User (
)
🔌 PointMethods is extension of Point (
)
Extension classes do not target aliases, generic instantiations, unions, or attestations.
Typical does not have a capitalized String object type. string is the string type.
Primitive Groups
Some primitive families are commonly extended together. The standard library provides a series of endowed primitive groups (defined in 03-Numbers) that can be extended.
🔌 IntegerFormatting is extension of AnyInteger (
toHexString() is string (
)
)
These groups are special standard library targets. They stand for a set of primitive types and avoid requiring the same extension to be written separately for each primitive.
Multiple Targets
An extension class can target multiple types with a comma-separated target list.
🔌 ReadableTools is extension of string, File, Buffer (
readText() is string (
)
)
This is repeated application of the same extension body to each target. It is not multiple inheritance.
When an extension applies to multiple targets, this has the union of the possible target types.
🔌 DebugTools is extension of string, User (
debugLabel() is string (
return this.toString()
)
)
This
Inside an extension member, this refers to the value being extended.
🔌 StringMethods is extension of string (
wrapped() is string (
return "[" + this + "]"
)
)
For primitive targets, this is passed by value. Primitive extension members cannot mutate the primitive value; they return a new value instead.
For class targets, an extension member can mutate public mutable properties on this, but this is discouraged. Extension methods should usually behave like ordinary helper behavior rather than hidden mutation.
Members
Extension classes can contain functions, getters, setters, and operator overload functions.
They cannot contain fields, constructors, static scopes, ghosts, or nested classes.
An extension operator overload is only allowed when the operator has not already been overloaded for the target.
Lookup
When resolving a member call, real members on the type win over extension members.
💡 User (
displayName() is string (
return "real"
)
)
🔌 UserDisplayExtensions is extension of User (
displayName() is string (
return "extension"
)
)
In this example, user.displayName() calls the real User function. Extension classes cannot overwrite real functions.
If more than one visible extension provides the same member, the last-defined extension member wins.
🔌 FirstUserExtensions is extension of User (
label() is string (
return "first"
)
)
🔌 SecondUserExtensions is extension of User (
label() is string (
return "second"
)
)
In this example, user.label() resolves to SecondUserExtensions.label.
When a program needs a specific extension member instead of the inferred one, it can attest the access path.
(user is FirstUserExtensions).label()
The attestation selects the extension member for lookup. It does not cast the value into an extension class, because extension classes do not exist as runtime or nominal types.
Scope
An extension class applies inside the scope where it is defined.
Non-exported extension classes remain local to their defining scope.
Extension classes are statically scoped method groups. They are not capabilities that an import activates, and importing a module has no special effect on extension lookup.
Inheritance
Extension classes do not participate in inheritance.
If an extension targets a base class, subclasses can use that extension through normal subtype compatibility.
💡 Animal (
eat() (
)
)
💡 Dog is Animal (
)
🔌 AnimalExtensions is extension of Animal
(
sleep() (
)
)
dog = Dog()
dog.sleep()
If the subclass already has a real member with the same name, the real member blocks the extension member.
Extensions cannot override inherited members. They also cannot call super.
To call a member from a compatible base view, attest this to that base type.
Animal (
makeSound() (
)
)
Dog is Animal (
makeSound() (
)
)
🔌 DogExtensions is extension of Dog (
makeAnimalSound()
(
(this is Animal).makeSound()
)
)
Packages
Any package can define extension classes for any type it can name, including types from another package.
Extension behavior is controlled by ordinary static scope. Packages do not globally patch foreign types merely by existing.
Limits
Generic extension classes are not supported.
Constrained extension classes are not supported.
Extension classes cannot be instantiated, inherited from, inherited by, or used as ordinary nominal types.