Adding behavior to existing types
Think of a phone case with a built-in card holder. You didn’t redesign the phone, and you couldn’t open it up if you wanted to, but now it does something it couldn’t before, and you use it as if it had always been part of the phone.
An extension does that for a type you can’t change, like String, Date or a class from a library: it adds new methods (and computed properties) that you then call with the usual dot syntax.
Swift extensions and Kotlin extension functions are compile-time sugar: Kotlin compiles fun String.initials() to a static method taking the receiver as its first parameter; Swift extension methods on non-@objc types are dispatched statically or via the type’s own witness tables, never inserted into a vtable. Neither can add stored state or change existing behavior. Java has no extensions (static utility methods are the idiom), and TypeScript’s equivalents (module augmentation, prototype patching) are either type-only or global mutation.
extension String {
var initials: String {
split(separator: " ")
.compactMap(\.first)
.map { String($0).uppercased() }
.joined()
}
}
print("ana marić".initials) // AMval String.initials: String
get() = split(" ")
.filter { it.isNotEmpty() }
.joinToString("") { it.first().uppercase() }
println("ana marić".initials) // AMfinal class Names {
private Names() {} // a holder for static helpers
static String initials(String name) {
return Arrays.stream(name.split(" "))
.filter(part -> !part.isEmpty())
.map(part -> part.substring(0, 1).toUpperCase())
.collect(Collectors.joining());
}
}
System.out.println(Names.initials("ana marić")); // AMexport function initials(name: string): string {
return name
.split(" ")
.filter((part) => part.length > 0)
.map((part) => part[0].toUpperCase())
.join("");
}
console.log(initials("ana marić")); // AM- Swift:
extension String { … }adds members toStringeverywhere in your module. Inside,selfis the string. - Kotlin:
fun String.initials()orval String.initialsdeclares an extension function or property. The type before the dot is the receiver; inside,thisis the string. - Java has no extensions, so the convention is a small class of static helper methods, called as
Names.initials(name). - TypeScript can’t safely add methods to built-in types, so a plain exported function is the norm.
The result is the same in all four: a reusable piece of behavior. Extensions just let you call it as name.initials, which reads more naturally and shows up in autocomplete.
TypeScript note
You can add a method to every string with String.prototype.initials = function () { … }, but it changes a built-in for every piece of code in the app, including libraries, and a future JavaScript version may add a method with the same name. Avoid it; see the last part.
Practice: from helper to extension
- Spot the pattern: a function whose first parameter is the thing it works on:
isWeekend(date),formatted(price). - Move the parameter in front of the name: Swift
extension Date { var isWeekend: Bool { … } }, Kotlinval LocalDate.isWeekend: Boolean get() = …. - Use
self/thisinstead of the parameter inside the body. - Call it with a dot:
date.isWeekend. In Java and TypeScript, keep the function and give it a clear name and home (Dates.isWeekend(date),isWeekend(date)).
isWeekend
Add isWeekend to a date type: Swift Date (use the current calendar), Kotlin java.time.LocalDate, Java as a static helper for LocalDate, TypeScript as a function for Date. Saturday and Sunday count as the weekend.
›
Swift: Calendar.current.isDateInWeekend(self). Kotlin and Java: dayOfWeek is a DayOfWeek enum. TypeScript: getDay() returns 0 for Sunday and 6 for Saturday.
SolutionTry it yourself first
extension Date {
var isWeekend: Bool { Calendar.current.isDateInWeekend(self) }
}
print(Date.now.isWeekend)val LocalDate.isWeekend: Boolean
get() = dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY
println(LocalDate.of(2026, 11, 8).isWeekend) // true: a Sundayfinal class Dates {
private Dates() {}
static boolean isWeekend(LocalDate date) {
DayOfWeek day = date.getDayOfWeek();
return day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY;
}
}
System.out.println(Dates.isWeekend(LocalDate.of(2026, 11, 8))); // trueexport function isWeekend(date: Date): boolean {
const day = date.getDay(); // 0 = Sunday … 6 = Saturday, in local time
return day === 0 || day === 6;
}
console.log(isWeekend(new Date(2026, 10, 8))); // true (months start at 0)Swift’s isDateInWeekend respects the user’s region (in some countries the weekend is Friday and Saturday); the other versions hard-code Saturday and Sunday. Which one is right depends on what the app means by “weekend”, a question worth asking before writing either.
What compiles to what
// Swift: an extension method is a normal method on the type, statically dispatched
// unless it satisfies a protocol requirement or is @objc dynamic.
extension String { func shout() -> String { uppercased() + "!" } }// Kotlin source
fun String.shout(): String = uppercase() + "!"
// What Java sees (file StringExtKt.kt → class StringExtKt):
// public static final String shout(String $this$shout)// Calling a Kotlin extension from Java:
String loud = StringExtKt.shout("hi");
// @file:JvmName("Strings") on the Kotlin file gives it a nicer class name.// TypeScript has no compile-time extensions; a function is the extension.
export const shout = (s: string): string => s.toUpperCase() + "!";Consequences: no access to private members of the receiver (Swift extensions in the same file can see private members; Kotlin extensions never see private or protected), no overriding, and resolution by the static type, which the next part shows.
- Swift
extension T { }and Kotlinfun T.f()/val T.p get()add methods and computed properties without subclassing; Kotlin compiles them to static methods with the receiver as the first parameter. - Java: static utilities in a
finalclass with a private constructor; Kotlin extensions appear asFileNameKt.f(receiver)(rename with@file:JvmName). - TypeScript: plain functions; module augmentation changes types only; prototype patching is global mutation.
- Swift extensions in the same file see
privatemembers; Kotlin extensions see only public/internal API.
Check this part
What does an extension let you do?
Inside
extension String { var initials: String { … } }, how do you refer to the string?Insideval String.initials get() = …, how do you refer to the string?Java has no extensions. What is the usual alternative?What is the safe way to add aninitialshelper for strings in TypeScript?How does Java see a Kotlin extension function
fun String.shout()declared in Strings.kt?