Masukkan Password
15 Fungsi sebagai Tipe
1. Mengingat konsep JavaScript: function bisa disimpan di variable
Misalnya kita punya function:
function greetUser(user: User) {
console.log("Hello " + user.name);
}
Function tersebut menerima satu parameter user.
Misalnya:
type User = {
name: string;
age: number;
};
Kemudian:
const user: User = {
name: "John",
age: 32
};
Kita bisa menjalankan:
greetUser(user);
Hasilnya:
Hello John
Sampai sini masih biasa.
2. Function bisa dimasukkan ke variable
JavaScript memungkinkan kita melakukan:
let greet = greetUser;
Artinya variable greet sekarang menyimpan reference ke function greetUser.
Jadi kita bisa melakukan:
greet(user);
Hasilnya tetap:
Hello John
Secara sederhana:
greetUser
↓
[ function ]
greet
↓
[ function yang sama ]
Jadi greet dan greetUser mengarah ke function yang sama.
3. Masalahnya: TypeScript bisa menganggap variable tersebut sebagai any
Dalam contoh lecture, ketika kita membuat:
let greet = greetUser;
TypeScript pada konteks tertentu bisa kehilangan informasi type yang kita inginkan, sehingga variable dapat menjadi terlalu longgar, misalnya any.
Kalau type-nya any, kita bisa melakukan hal seperti:
greet = 100;
Bahkan:
greet = "hello";
atau:
greet = true;
TypeScript tidak akan mempermasalahkannya karena:
any
kurang lebih berarti:
“Terserah, variable ini boleh berisi apa saja.”
4. Kenapa any berbahaya?
Misalnya:
let greet: any = greetUser;
Awalnya:
greet → greetUser function
Lalu kita melakukan:
greet = 100;
Sekarang:
greet → 100
Kemudian kita melakukan:
greet(user);
Kita sedang mencoba memperlakukan angka 100 seperti function.
Ini salah.
Tetapi TypeScript tidak memperingatkan kita karena greet adalah any.
Akhirnya error baru muncul ketika program dijalankan:
TypeError: greet is not a function
5. Compile-time error vs runtime error
Ini bagian yang sangat penting.
Ada dua jenis error yang perlu kita pahami:
Compile-time error
Error yang diketahui TypeScript sebelum program dijalankan.
Contohnya:
let age: number = "hello";
TypeScript akan langsung mengatakan:
Type 'string' is not assignable to type 'number'
Ini bagus karena kita bisa memperbaikinya sebelum program berjalan.
Runtime error
Error yang baru terjadi ketika program benar-benar dijalankan.
Contohnya:
let greet: any = greetUser;
greet = 100;
greet(user);
TypeScript tidak complain saat compile.
Tetapi ketika JavaScript dijalankan:
greet is not a function
Jadi salah satu tujuan TypeScript adalah:
sebisa mungkin memindahkan error dari runtime ke compile time.
6. Solusinya: beri type pada variable greet
Kita ingin memberitahu TypeScript:
“Variable
greetini harus selalu berisi function.”
Kita bisa menulis:
let greet: Function;
Sekarang:
greet = greetUser;
boleh.
Tetapi:
greet = 100;
tidak boleh.
Karena 100 bukan function.
Jadi:
let greet: Function;
greet = greetUser; // ✅
greet = 100; // ❌
greet = "hello"; // ❌
greet = true; // ❌
Ini sudah lebih aman.
Tapi masih ada masalah.
7. Masalah dengan type Function
Perhatikan:
let greet: Function;
Kita hanya mengatakan:
“greet harus berupa function.”
Tapi kita belum mengatakan function seperti apa.
Misalnya ada function lain:
function sum(num1: number, num2: number) {
return num1 + num2;
}
Function ini juga merupakan function.
Jadi TypeScript akan mengizinkan:
greet = sum;
Padahal kita sebenarnya ingin greet hanya menerima function dengan bentuk tertentu.
8. Apa maksud “function dengan bentuk tertentu”?
Kita ingin greet hanya boleh diisi function yang:
- menerima satu parameter
- parameter tersebut bertipe
User - tidak mengembalikan value
Dengan kata lain kita ingin mengatakan:
greet harus menerima:
User
dan menghasilkan:
void
Misalnya:
function greetUser(user: User): void {
console.log("Hello " + user.name);
}
Function ini cocok.
Tapi:
function sum(num1: number, num2: number): number {
return num1 + num2;
}
tidak cocok.
Karena bentuknya berbeda.
9. Inilah yang disebut Function Signature
Konsep penting dalam lecture ini adalah:
Function Signature
Function signature bisa kita bayangkan sebagai cetak biru / kontrak sebuah function.
Misalnya kita punya:
(user: User) => void
Artinya:
“Function ini harus menerima satu parameter bertipe
Userdan tidak mengembalikan value.”
Mari kita pecah:
(user: User) => void
Bagian:
user: User
berarti function menerima parameter user dengan type User.
Sedangkan:
void
berarti function tersebut tidak mengembalikan value.
10. Memberikan Function Signature kepada variable
Sekarang kita bisa menulis:
let greet: (user: User) => void;
Ini sangat berbeda dengan:
let greet: Function;
Karena:
Function
berarti:
“Pokoknya function.”
Sedangkan:
(user: User) => void
berarti:
“Harus function yang menerima satu
Userdan returnvoid.”
11. Function greetUser cocok
Misalnya:
function greetUser(user: User): void {
console.log("Hello " + user.name);
}
Kita punya:
let greet: (user: User) => void;
Kemudian:
greet = greetUser;
Ini valid:
greetUser
parameter : User
return : void
sedangkan requirement greet:
parameter : User
return : void
Sama persis.
Jadi:
greet = greetUser; // ✅
12. Function sum tidak cocok
Sekarang kita punya:
function sum(num1: number, num2: number): number {
return num1 + num2;
}
Lalu:
greet = sum;
TypeScript akan menolak.
Kenapa?
Karena greet membutuhkan:
(user: User) => void
Sedangkan sum memiliki bentuk:
(num1: number, num2: number) => number
Bandingkan:
greet membutuhkan | sum | |
|---|---|---|
| Jumlah parameter | 1 | 2 |
| Parameter | User | number, number |
| Return | void | number |
Jelas berbeda.
Maka:
greet = sum; // ❌
TypeScript memberikan error saat compile-time.
Dan ini yang kita inginkan.
13. Contoh function lain yang cocok
Misalnya kita membuat:
function isEligible(user: User): void {
console.log(user.age >= 18);
}
Signature-nya:
(User) => void
Jadi cocok dengan:
let greet: (user: User) => void;
Kita boleh:
greet = greetUser;
dan:
greet = isEligible;
Karena keduanya memenuhi kontrak.
14. Analogi sederhana: colokan listrik 🔌
Bayangkan variable greet seperti sebuah socket/colokan yang punya aturan tertentu.
Kita bilang:
let greet: (user: User) => void;
Artinya socket ini hanya menerima “charger” dengan spesifikasi:
Input : User
Output : void
Function:
greetUser(user: User): void
cocok.
Jadi:
greetUser
↓
[ User → void ]
↓
cocok dengan socket greet
Function:
sum(num1: number, num2: number): number
tidak cocok.
Karena:
sum
↓
[ number, number → number ]
Spesifikasinya berbeda.
15. Parameter name tidak harus sama
Ada satu detail yang disebut lecture.
Misalnya kita menulis:
let greet: (user: User) => void;
Kita sebenarnya tidak terlalu peduli dengan nama parameter.
Function ini:
function greetUser(user: User): void {
console.log(user.name);
}
cocok.
Function ini juga secara konsep memiliki signature yang sama:
function anotherFunction(person: User): void {
console.log(person.name);
}
Nama:
user
vs
person
tidak menjadi masalah.
Yang penting:
parameter type = User
return type = void
Jadi:
(user: User) => void
dan:
(person: User) => void
memiliki bentuk function yang sama dari sudut pandang type.
16. Tapi return type juga penting
Misalnya:
function getName(user: User): string {
return user.name;
}
Function ini menerima User, tetapi return-nya string.
Sedangkan greet membutuhkan:
(user: User) => void
Jadi:
greet = getName;
tidak sesuai dengan kontrak yang kita inginkan.
Kenapa?
Karena:
greet membutuhkan:
User → void
sedangkan:
getName:
User → string
Return type-nya berbeda.
17. Cara membaca syntax (user: User) => void
Awalnya syntax ini memang terlihat aneh.
(user: User) => void
Jangan menganggap ini sebagai function biasa yang sedang kita buat.
Di sini kita sedang mendeskripsikan type dari sebuah function.
Baca dari kiri ke kanan:
(user: User)
“function menerima parameter user bertipe User”
lalu:
=>
“dan function tersebut…”
lalu:
void
“…menghasilkan return type void.”
Jadi:
(user: User) => void
dibaca:
Function yang menerima satu parameter bertipe
Userdan return type-nyavoid.
18. Jangan tertukar dengan Arrow Function biasa
Ada dua syntax yang kelihatannya mirip.
Arrow function biasa
const greet = (user: User) => {
console.log(user.name);
};
Ini adalah function implementation.
Kita benar-benar membuat function.
Function type
let greet: (user: User) => void;
Ini bukan membuat function.
Ini hanya mengatakan:
“
greetharus berisi function dengan bentuk seperti ini.”
Jadi bedanya:
const greet = (user: User) => {
console.log(user.name);
};
→ membuat function.
Sedangkan:
let greet: (user: User) => void;
→ menentukan type sebuah variable.
19. Flow lengkap contoh lecture
Mari kita gabungkan semuanya.
Pertama:
type User = {
name: string;
age: number;
};
Kemudian function pertama:
function greetUser(user: User): void {
console.log("Hello " + user.name);
}
Function kedua:
function isEligible(user: User): void {
console.log(user.age >= 18);
}
Function ketiga:
function sum(num1: number, num2: number): number {
return num1 + num2;
}
Sekarang kita membuat variable dengan function signature:
let greet: (user: User) => void;
Maka:
greet = greetUser; // ✅
boleh.
Kemudian:
greet(user);
akan menjalankan greetUser.
Lalu kita bisa mengganti function:
greet = isEligible; // ✅
Kemudian:
greet(user);
sekarang menjalankan isEligible.
Tetapi:
greet = sum; // ❌
ditolak TypeScript.
20. Jadi variable greet bisa menunjuk ke function yang berbeda
Ini konsep yang sangat menarik.
Variable:
greet
tidak selalu harus menunjuk ke function yang sama.
Awalnya:
greet = greetUser;
sehingga:
greet
↓
greetUser
Kemudian:
greet = isEligible;
sekarang:
greet
↓
isEligible
Tapi TypeScript memastikan bahwa function yang dimasukkan harus memenuhi signature:
(user: User) => void
Jadi variable tersebut fleksibel, tetapi tetap aman.
21. Kenapa ini berguna?
Ini sangat berguna ketika kita ingin membuat program yang fleksibel tetapi tetap type-safe.
Misalnya kita punya:
function processUser(
user: User,
callback: (user: User) => void
) {
callback(user);
}
Kita bisa mengirim function yang berbeda:
processUser(user, greetUser);
atau:
processUser(user, isEligible);
Karena kedua function tersebut memiliki signature:
(User) => void
Ini adalah konsep yang nantinya sering muncul dalam:
- callback
- event handler
- array methods
- asynchronous programming
- API
- React
- dan banyak bagian JavaScript/TypeScript lainnya.
Contoh yang mungkin sudah pernah kamu lihat:
users.forEach((user) => {
console.log(user.name);
});
Function yang diberikan kepada forEach adalah contoh penggunaan function sebagai value/callback.
22. Hubungannya dengan materi sebelumnya
Kalau kita rangkum perjalanan materi kita:
Sebelumnya: parameter type
function greet(user: User) {
}
Kita menentukan type parameter.
Kemudian: return type
function getUser(): User {
return user;
}
Kita menentukan type return.
Sekarang: function type
let greet: (user: User) => void;
Kita menentukan type dari variable yang menyimpan function.
Jadi sekarang kita sudah mengenal tiga hal:
FUNCTION
│
┌──────────┼──────────┐
↓ ↓ ↓
Parameter Return Function Type
│ │ │
User User (User) => void
23. Function vs Function Signature
Ini salah satu bagian paling penting untuk diingat.
Terlalu umum:
let fn: Function;
Artinya:
fnharus berupa function, tetapi function-nya seperti apa tidak ditentukan.
Lebih spesifik:
let fn: (user: User) => void;
Artinya:
fnharus berupa function yang menerimaUserdan menghasilkanvoid.
Jadi sebisa mungkin, kalau kita tahu bentuk function yang dibutuhkan, lebih baik menggunakan function signature yang spesifik daripada sekadar Function.
24. Analogi “kontrak”
Cara paling mudah mengingatnya adalah dengan konsep kontrak.
Ketika kita menulis:
let greet: (user: User) => void;
kita sedang membuat kontrak:
“Siapa pun yang mau masuk ke variable
greet, harus berupa function yang menerimaUserdan returnvoid.”
Maka:
function greetUser(user: User): void {}
memenuhi kontrak ✅
function isEligible(user: User): void {}
memenuhi kontrak ✅
Tetapi:
function sum(a: number, b: number): number {}
tidak memenuhi kontrak ❌
25. Kesimpulan
Inti lecture ini sebenarnya hanya beberapa konsep utama:
1. Function adalah value
Di JavaScript/TypeScript, function bisa disimpan dalam variable:
let greet = greetUser;
2. Variable yang menyimpan function juga bisa diberi type
let greet: Function;
Ini memastikan greet hanya boleh berisi function.
3. Tetapi Function masih terlalu umum
Karena kita belum menentukan function tersebut harus menerima parameter apa dan menghasilkan apa.
4. Kita bisa menentukan Function Signature
let greet: (user: User) => void;
Artinya:
Parameter : User
Return : void
5. Function yang signature-nya cocok boleh diberikan
greet = greetUser; // ✅
greet = isEligible; // ✅
Sedangkan function dengan signature berbeda:
greet = sum; // ❌
akan ditolak TypeScript.
🧠 Cara paling gampang mengingat
Bayangkan:
let greet: (user: User) => void;
sebagai sebuah slot khusus function.
Slot tersebut berkata:
“Saya hanya menerima function yang bentuknya
User → void.”
Jadi:
greetUser
User → void
↓
✅
↓
greet
isEligible
User → void
↓
✅
↓
greet
Tetapi:
sum
number, number → number
↓
❌
↓
greet
Kalimat paling penting dari lecture ini:
Kita tidak hanya bisa memberi type pada parameter dan return function; kita juga bisa memberi type pada variable yang menyimpan function, bahkan menentukan secara spesifik parameter dan return type function tersebut melalui Function Signature.