Fungsi itu ibarat mesin kecil: kamu masukkan beberapa nilai, mesin itu bekerja sebentar, lalu mengembalikan sesuatu. Di JavaScript biasa, tidak ada yang memeriksa apa yang kamu masukkan atau apa yang keluar. Kamu bisa saja memanggil fungsi yang sebenarnya untuk angka dengan sebuah teks, dan kesalahannya baru ketahuan saat program sudah berjalan dan tingkahnya jadi aneh. TypeScript mengubah keadaan ini. Dengan menambahkan tipe pada input dan output sebuah fungsi, kamu menjadikan fungsi itu sebuah kontrak yang bisa ditegakkan oleh editor dan compiler bahkan sebelum kodemu dijalankan.
Kalau kamu sudah pernah mengenal fungsi di JavaScript biasa, artikel ini langsung melanjutkan dari sana. Bentuknya sama; kita cuma menambahkan label pada tiap kolomnya. Kalau mau menyegarkan ingatan soal bagaimana fungsi bekerja sejak awal, sisi JavaScript-nya ada di sini: Fungsi JavaScript.
Memberi tipe pada parameter
Parameter adalah nilai yang diharapkan diterima oleh sebuah fungsi. Di TypeScript kamu menuliskan tipe yang diharapkan tepat setelah nama parameter, dipisahkan oleh titik dua.
function greet(name: string) {
return "Halo, " + name;
}
greet("Jane"); // aman
greet(42); // error: number tidak bisa dipakai sebagai string
Panggilan kedua itu tidak akan pernah lolos dari compiler. TypeScript membaca name: string sebagai sebuah janji: kolom ini hanya menerima teks. Begitu kamu memasukkan angka, editor langsung menggarisbawahinya dengan warna merah dan memberi tahu persis apa yang salah. Kesalahannya ketangkap sambil kamu mengetik, bukan saat pengguna menekan tombol di produksi.
Setiap parameter diberi tipe secara terpisah, jadi satu fungsi bebas mencampur beberapa tipe sekaligus.
function repeat(text: string, times: number) {
return text.repeat(times);
}
repeat("ab", 3); // "ababab"
Kalau kamu lupa memberi tipe pada sebuah parameter, TypeScript akan jatuh ke tipe khusus bernama any, yang artinya “apa saja boleh, berhenti memeriksa.” Biasanya ini bukan yang kamu mau, karena pemeriksaan keamanan untuk nilai itu jadi mati. Banyak proyek menyalakan pengaturan yang menandai parameter tanpa tipe supaya kamu tidak kehilangan perlindungan tanpa sengaja.
Memberi tipe pada nilai kembalian
Tipe kembalian menggambarkan apa yang dikembalikan fungsi. Kamu menuliskannya setelah daftar parameter, lagi-lagi dengan titik dua.
function add(a: number, b: number): number {
return a + b;
}
Di sini : number setelah tanda kurung berarti “fungsi ini mengembalikan sebuah number.” Kalau badan fungsinya sampai mengembalikan sesuatu yang lain, TypeScript akan protes.
Kamu tidak selalu harus menulis tipe kembalian sendiri. TypeScript jago menyimpulkan tipe itu — ia melihat isi fungsi lalu mencari tahu jawabannya. Pada add, ia sudah tahu dua number yang dijumlahkan menghasilkan number, jadi anotasinya opsional. Meski begitu, menuliskannya tetap punya manfaat nyata: ia mendokumentasikan maksudmu dan langsung menangkapmu kalau isi fungsi menyimpang dari yang kamu janjikan.
Kapan menulis tipe kembalian secara manual
Untuk fungsi bantu kecil yang dipakai internal, biarkan penyimpulan tipe yang bekerja. Untuk fungsi publik yang diandalkan bagian lain aplikasimu, tulis tipe kembaliannya secara eksplisit. Itu berfungsi sebagai pagar pengaman: kalau kamu tanpa sengaja mengubah apa yang dikembalikan fungsi, errornya muncul di dalam fungsi itu sendiri, bukan di file lain nun jauh yang memanggilnya.
Parameter opsional dan default
Kadang sebuah argumen tidak selalu dibutuhkan. Tandai parameter sebagai opsional dengan tanda tanya setelah namanya. Parameter opsional bisa saja tidak ada, jadi tipenya otomatis mencakup undefined.
function createUser(name: string, nickname?: string) {
if (nickname) {
return name + " (" + nickname + ")";
}
return name;
}
createUser("Jane Doe"); // aman, nickname tidak diisi
createUser("John Doe", "Johnny"); // aman, nickname diisi
Saudara dekatnya adalah parameter default: sebuah nilai yang dipakai ketika pemanggil tidak mengisi argumen itu. Kamu bahkan tidak perlu memberinya tipe, karena TypeScript membaca tipenya dari nilai default tadi.
function connect(host: string, port = 8080) {
return `${host}:${port}`;
}
connect("acy-partner.com"); // "acy-partner.com:8080"
connect("acy-partner.com", 3000); // "acy-partner.com:3000"
Karena port = 8080 adalah sebuah number, port otomatis bertipe number tanpa perlu kamu sebutkan. Ada satu aturan penting: parameter opsional dan default ditaruh di akhir daftar. Parameter wajib tidak boleh berada setelah parameter opsional, sebab pemanggil jadi tidak punya cara untuk melewati yang opsional dengan rapi.
Rest parameter
Bagaimana kalau kamu tidak tahu berapa banyak argumen yang akan datang? Sebuah rest parameter mengumpulkan semua sisa argumen ke dalam satu array. Kamu menuliskan tiga titik sebelum namanya, lalu memberinya tipe array.
function sum(...numbers: number[]): number {
let total = 0;
for (const n of numbers) {
total += n;
}
return total;
}
sum(1, 2, 3); // 6
sum(10, 20, 30, 40); // 100
Karena numbers bertipe number[] (sebuah array berisi number), TypeScript memeriksa setiap argumen yang kamu kirim. Selipkan sebuah teks di situ dan kamu langsung dapat error yang menunjuk tepat ke teks itu. Rest parameter selalu menjadi parameter terakhir, karena ia menyapu semua yang tersisa.
Ekspresi tipe fungsi
Sampai sini kita sudah memberi tipe pada isi sebuah fungsi. Tapi fungsi juga termasuk nilai — kamu bisa menyimpannya di variabel dan mengopernya ke fungsi lain. Untuk menggambarkan bentuk sebuah fungsi sebagai nilai, TypeScript memakai ekspresi tipe fungsi. Bentuknya seperti sebuah panah:
(x: number) => string
Bacanya begini: “sebuah fungsi yang menerima satu number dan mengembalikan sebuah string.” Perhatikan, panah => di sini adalah bagian dari sebuah tipe yang menggambarkan fungsi — ini bukan panah yang sama dengan panah pada arrow function, walaupun tampilannya mirip.
Ini paling berguna saat sebuah fungsi menerima fungsi lain (sebuah callback). Kamu bisa menjabarkan persis seperti apa callback itu harus berbentuk.
function transform(value: number, fn: (x: number) => string): string {
return fn(value);
}
transform(42, (n) => "Angka: " + n); // "Angka: 42"
Parameter fn diwajibkan berupa fungsi dari number ke string. Oper fungsi dengan bentuk yang salah, dan TypeScript menolaknya sebelum program berjalan.
| Bagian | Artinya |
|---|---|
(x: number) |
fungsinya menerima satu parameter number |
=> |
“mengembalikan” |
string |
tipe yang dikembalikan |
Kamu juga bisa memberi nama pada bentuk-bentuk ini lewat type alias supaya tanda tangan fungsinya lebih enak dibaca:
type Formatter = (x: number) => string;
function transform(value: number, fn: Formatter): string {
return fn(value);
}
Tipe kembalian void
Sebagian fungsi memang tidak mengembalikan nilai yang berguna — mereka hanya melakukan sesuatu, seperti mencatat pesan atau menyimpan data. Tipe kembaliannya adalah void, yang artinya “fungsi ini tidak mengembalikan apa pun yang berarti.”
function logMessage(message: string): void {
console.log(message);
}
void adalah caramu memberi tahu pembaca (dan compiler) bahwa hasilnya memang tidak dimaksudkan untuk dipakai. Kalau kamu mencoba menampung hasil fungsi void ke sebuah variabel yang mengharapkan nilai sungguhan, TypeScript akan memperingatkanmu. Inilah beda antara fungsi yang menjawab pertanyaan dan fungsi yang menjalankan sebuah tindakan.
Sekilas tentang overload
Sesekali sebuah fungsi tunggal perlu berperilaku berbeda tergantung apa yang ia terima. Overload memungkinkanmu mendeklarasikan beberapa tanda tangan panggilan untuk satu fungsi, sehingga pemanggil mendapat tipe yang tepat untuk tiap kasus.
function parseInput(value: string): string[];
function parseInput(value: number): number;
function parseInput(value: string | number): string[] | number {
if (typeof value === "string") {
return value.split(",");
}
return value * 2;
}
const a = parseInput("a,b,c"); // bertipe string[]
const b = parseInput(10); // bertipe number
Dua baris pertama adalah tanda tangan overload — versi yang dilihat para pemanggil. Baris ketiga adalah implementasi sesungguhnya, yang harus menangani semua kasus namun tetap tersembunyi dari pemanggil. Overload adalah alat tingkat lanjut, dan sebagian besar waktu satu tanda tangan yang jelas (atau sebuah union type) sudah cukup. Pakai overload hanya kalau fungsi memang benar-benar mengubah tipe kembaliannya berdasarkan inputnya.
Overload itu pilihan terakhir
Overload cepat menambah kerumitan. Sebelum menulisnya, tanyakan dulu apakah desain yang lebih sederhana — memecahnya jadi dua fungsi bernama, atau menerima satu tipe yang konsisten — justru lebih membantu pembaca. Jelas lebih baik daripada pintar.
Bagaimana fungsi bertipe menangkap kesalahan di tempat pemanggilan
Buah dari semua pemberian tipe ini muncul tepat di tempat pemanggilan — lokasi di mana kamu benar-benar memakai fungsi itu. Karena TypeScript tahu persis bentuk yang diharapkan sebuah fungsi, ia memeriksa setiap panggilan terhadap bentuk tersebut.
add(a: number, b: number): number
add(2, 3) ✓ dua number, aman
add(2, "3") ✗ "3" itu string, bukan number
add(2) ✗ argumen kedua hilang
add(2, 3, 4) ✗ kelebihan satu argumen
Setiap panggilan keliru itu ditandai di editormu dengan pesan yang jelas, dan proses build menolak untuk lanjut. Di JavaScript biasa, add(2, "3") akan diam-diam menghasilkan "23" — sebuah string, bukan number — dan kamu mungkin tidak sadar sampai ada sesuatu di hilir yang rusak dengan cara yang membingungkan. Fungsi bertipe memindahkan penemuan masalah itu dari saat program berjalan, ketika bug mahal dan sulit dilacak, ke saat menyunting, ketika bug murah dan terang-terangan.
Rangkuman
Fungsi di TypeScript adalah fungsi JavaScript biasa yang tiap kolomnya diberi label dengan jelas. Inilah yang perlu kamu bawa pulang:
- Tipe parameter (
name: string) menyatakan harus berupa apa setiap input. - Tipe kembalian (
: number) menyatakan apa yang keluar; TypeScript sering bisa menyimpulkannya, tapi menuliskannya untuk fungsi publik adalah pagar pengaman yang berguna. - Parameter opsional (
nickname?) dan default (port = 8080) menangani argumen yang tidak selalu diisi — taruh di akhir. - Rest parameter (
...numbers: number[]) mengumpulkan argumen sisa sebanyak apa pun ke dalam satu array bertipe. - Ekspresi tipe fungsi (
(x: number) => string) menggambarkan fungsi yang dipakai sebagai nilai, pas sekali untuk callback. voidmenandai fungsi yang melakukan sesuatu, bukan mengembalikan sesuatu.- Overload membuat satu fungsi menampilkan beberapa tanda tangan — ampuh, tapi simpan untuk saat kamu benar-benar membutuhkannya.
Biasakan memberi tipe pada fungsimu dan kamu akan langsung merasakan bedanya: lebih sedikit kejutan, kode yang lebih jelas, dan editor yang menangkap kesalahanmu tepat saat kamu membuatnya. Kalau mau menengok lagi bagaimana fungsi bekerja di balik semua label ini, dasar-dasar JavaScript adalah perhentian berikutnya yang bagus: Fungsi JavaScript.