What is function overloading?
Function overloading is the process of redefining a function with the same name but different parameter combinations. The compiler or type system determines the correct function to call based on the parameters.
Function overloading in OOP languages
Java ve C++ gibi dillerde overload işlemi doğrudan desteklenir. Derleyici, çağrılan fonksiyonun parametrelerine göre doğru metodu seçer.
Java’da implementasyonu aşağıdaki gibidir:
class Notification { void send(String email, String message) { // ... }
void send(String email, String message, boolean isUrgent) { // ... }
void send(String email, String subject, String body) { // ... }}
public class Main { public static void main(String[] args) { Notification notifier = new Notification();
notifier.send("Hello!"); notifier.send( "Reminder", "The meeting time has been changed to 10 AM." ); notifier.send( "ensbaspinar@gmail.com", "Can you send me the plans for the team?", true ); }}
C++ dilinde de benzer bir implementasyon mevcuttur:
class Notification {public: void send(const string& message) { // ... }
void send(const string& subject, const string& body) { // ... }
void send(const string& email, const string& message, bool isUrgent) { // ... }};
int main() { Notification notifier;
notifier.send("Hello!"); notifier.send( "Reminder", "The meeting time has been changed to 10 AM." ); notifier.send( "ensbaspinar@gmail.com", "Can you send me the plans for the team?", true );}
Bu örneklerde send
metodu farklı sayıda ve türde parametrelerle çağrılabilir.
How does function overloading work?
- Derleyici, aynı isme sahip fonksiyonların imzalarını kontrol eder. Eğer bu fonksiyonların parametre listeleri farklıysa, derleme başarılı olur; aksi takdirde derleme hatası oluşur.
- Aynı isme sahip fonksiyonları ayırt etmek için name mangling tekniğini kullanır. Bu teknik, fonksiyon adlarını parametre sayısı ve türleriyle birlikte kodlayarak benzersiz hale getirir. Örneğin,
foo(int, double)
vefoo(double, double)
fonksiyonlarının isimleri derleyicidefoo_id
vefoo_dd
olarak temsil edilir. - Fonksiyon çağrıldığında derleyici argüman türlerini kontrol eder ve uygun fonksiyonu bulur.
Function overloading in TypeScript
TypeScript doğrudan OOP dilleri gibi overload desteklemez. Tek bir implementasyona birdan fazla imza type’ı tanımlamanızı sağlar.
Example
class Notification { send(message: string): void; send(subject: string, body: string): void; send(email: string, message: string, isUrgent?: boolean): void; send(arg1: unknown, arg2?: unknown, arg3?: unknown): void { // İmza send(message: string) ise if (typeof arg1 === "string" && !arg2 && !arg3) { // ... }
// İmza send(subject: string, body: string) ise else if (typeof arg1 === "string" && typeof arg2 === "string" && !arg3) { // ... }
// İmza send(email: string, message: string, isUrgent?: boolean) ise else if (typeof arg1 === "string" && typeof arg2 === "string" && typeof arg3 === "boolean") { // ... } }}
const notifier = new Notification();
notifier.send("Hello!");notifier.send("Hello!");notifier.send( "Reminder", "The meeting time has been changed to 10 AM.");notifier.send( "ensbaspinar@gmail.com", "Can you send me the plans for the team?", true);
Example 2
Geçtiğimiz aylarda şirkettekullandığım ve beni bu hook’u yazmaya iten hook örneğini de eklemiş olayım.
type Callback = () => void;
export function useSeenObserver(callback: Callback): React.RefObject<HTMLElement>;export function useSeenObserver(selector: string, callback: Callback): void;export function useSeenObserver(arg1: string | Callback, arg2?: Callback): React.RefObject<HTMLElement> | void { let element: Element; let callback: Callback; let ref = useRef<HTMLElement>(null);
if (typeof arg1 === "string") { element = document.querySelector(arg1)!; callback = arg2!; } else { element = ref.current!; callback = arg1!; }
// ...
if (typeof arg1 !== "string") { return ref; }}
Hook’u kullanırken return ettiğimiz ref’i kullanarak ilgili elementi belirtebilirsiniz.
const ref = useSeenObserver(() => { // ...});
return <div ref={ref}></div>;
Ya da aşağıdaki gibi bir selector kullanabilirsiniz.
useSeenObserver("#component", () => { // ...});
return <div id="component"></div>;
How does function overloading work?
- Fonksiyonun belirtilen imzalardan biriyle uyumlu olup olmadığını görmek için geliştirme ve derleme esnasında tür kontrolleri yapılarak uyarır.
- Typescript, JavaScript’e derlendiğinden dolayı type safety ve signature check işlemleri runtime’da çalışmaz. Bu sebepten ötürü fonksiyonun içerisinde parametre türlerinin koşullarla kontrol edilir.
Why should I use it?
Aynı fonksiyonu union type ile tanımlamak da mümkündür ve bu sebeple neden signature kullanmalıyım sorusu aklınıza gelebilir. Son örneği signature kullanmadan yeniden yazalım.
type Callback = () => void;
export function useSeenObserver(arg1: string | Callback, arg2?: Callback): React.RefObject<HTMLElement> | void { let element: Element; let callback: Callback; let ref = useRef<HTMLElement>(null);
if (typeof arg1 === "string") { element = document.querySelector(arg1)!; callback = arg2!; } else { element = ref.current!; callback = arg1!; }
// ...
if (typeof arg1 !== "string") { return ref; }}
Bu implementasyonu kullandığımızda TypeScript’in önerdiği tipe ve yaptığı kontrole görseldeki gibidir.
Görebileceğiniz üzere net bir template gösteremez. Argüman isimleri ve hangi parametrenin hangisiyle birlikte kullanılabileceği bulanıklaşır. Aşağıdaki hiçbir kombinasyonda TypeScript hata vermez:
-
(arg1: string)
-> hatalı kullanım -
(arg1: string, arg2: Callback)
-> doğru kullanım -
(arg1: Callback)
-> doğru kullanım -
(arg1: Callback, arg2: Callback)
-> hatalı kullanım
Signature kullandığınızda ise aşağıdaki gibi doğru bir API elde etmiş olursunuz.
Function overloading, özellikle büyük projelerde hatalara karşı dayanıklı bir API tasarımı sağlar. Ama OOP dillerine göre manual kontrollere güvenir ve kodu kirletir. Bu pattern ile genelde çok fazla karşılaşılmaz.
Diğer yazılarda buluşalım 👋