kodingup.camp

Beranda/TypeScript Mastery

Function Bertipe

Modul 4 dari 124 menit bacaTyped Functions

Isi modul

    Kenapa ini penting

    Function adalah tempat bug paling sering lahir: argumen tertukar urutannya, lupa mengirim parameter, atau mengembalikan undefined di salah satu cabang if. Di JavaScript, hitungDiskon(harga, persen) dan hitungDiskon(persen, harga) sama-sama "jalan". Di TypeScript, tipe parameter adalah kontrak: siapa pun yang memanggil function harus memenuhi kontrak itu, dan function sendiri wajib menepati janjinya soal nilai kembalian.

    Bayangkan mesin fotokopi di kantor. Di bagian atasnya ada tulisan "Masukkan kertas A4, maksimal 50 lembar, hasilnya salinan A4". Itu kontrak. Kalau kamu memasukkan kardus, mesin menolak sebelum macet. Tipe parameter dan return adalah tulisan di mesin fotokopi itu, dan karena kontraknya tertulis, editor bisa menunjukkan kontrak tersebut setiap kali kamu mengetik nama function.

    Konsep inti

    Parameter dan return type

    function hitungDiskon(harga: number, persen: number): number {
      return harga - (harga * persen) / 100;
    }
    

    Tipe parameter wajib ditulis (di mode strict, parameter tanpa tipe dianggap any implisit dan menghasilkan error TS7006). Tipe kembalian bisa ditebak oleh TypeScript, tapi menuliskannya untuk function yang diekspor membuat niatmu jelas dan menangkap bug di dalam function, misalnya cabang yang lupa return.

    Arrow function dan function expression

    const kali = (a: number, b: number): number => a * b;
    
    // Tipe function ditulis terpisah, lalu dipakai
    type Operasi = (a: number, b: number) => number;
    const bagi: Operasi = (a, b) => a / b; // a dan b otomatis number
    

    Perhatikan baris terakhir: karena bagi sudah diberi tipe Operasi, parameter a dan b tidak perlu dianotasi lagi. Ini disebut contextual typing, dan inilah sebabnya callback di .map() atau onClick jarang perlu tipe eksplisit.

    Parameter opsional dan default

    function sapa(nama: string, gelar?: string): string {
      return gelar ? `Halo, ${gelar} ${nama}` : `Halo, ${nama}`;
    }
    
    function formatRupiah(angka: number, denganSimbol = true): string {
      const teks = angka.toLocaleString("id-ID");
      return denganSimbol ? `Rp${teks}` : teks;
    }
    

    Rest parameter

    function jumlahkan(...angka: number[]): number {
      return angka.reduce((a, b) => a + b, 0);
    }
    

    Parameter object (destructuring)

    Kalau parameter lebih dari tiga, lebih aman pakai satu object. Urutan tidak bisa tertukar dan pemanggilan lebih mudah dibaca:

    type OpsiPesan = { tujuan: string; isi: string; penting?: boolean };
    
    function kirimPesan({ tujuan, isi, penting = false }: OpsiPesan): void {
      console.log(`${penting ? "[!] " : ""}Ke ${tujuan}: ${isi}`);
    }
    

    Callback type

    Function yang menerima function lain perlu menjelaskan bentuk callback-nya:

    function ulangi(kali: number, aksi: (ke: number) => void): void {
      for (let i = 1; i <= kali; i++) aksi(i);
    }
    

    Tipe kembalian void pada callback punya arti khusus: "nilai kembaliannya tidak akan dipakai". Karena itu ulangi(3, (i) => daftar.push(i)) tetap boleh walaupun push mengembalikan angka.

    Function overload

    Kadang satu function menerima beberapa bentuk input dan tipe kembaliannya bergantung pada input. Overload menuliskan beberapa "tanda tangan" (signature) di atas satu implementasi:

    function ubah(nilai: string): number;
    function ubah(nilai: number): string;
    function ubah(nilai: string | number): string | number {
      return typeof nilai === "string" ? Number(nilai) : String(nilai);
    }
    

    Pemanggil hanya melihat dua signature pertama. Implementasinya (yang ketiga) tidak terlihat dari luar. Overload berguna tapi jangan berlebihan; sering kali union atau generics (modul Generics) lebih sederhana.

    Function async

    Function async selalu mengembalikan Promise. Return type-nya ditulis Promise<T>, di mana T adalah nilai yang kamu return di dalamnya:

    async function ambilJumlahPesanan(tokoId: number): Promise<number> {
      const daftar = [3, 5, 2]; // anggap hasil dari server untuk toko ini
      return daftar.length + tokoId * 0;
    }
    
    ambilJumlahPesanan(1).then((n) => console.log(n.toFixed(0))); // → 3
    

    Kalau kamu lupa await dan memperlakukan hasilnya seperti angka biasa, misalnya ambilJumlahPesanan(1) + 1, TypeScript menolak karena Promise<number> bukan number. Ini menangkap salah satu bug async paling umum, yaitu lupa await, jauh sebelum aplikasi berjalan.

    Membaca signature di editor

    Salah satu manfaat terbesar function bertipe adalah dokumentasi gratis. Saat kamu mengetik hitungDiskon(, editor menampilkan hitungDiskon(harga: number, persen: number): number. Kamu tidak perlu membuka file aslinya untuk tahu urutan argumen. Biasakan memberi nama parameter yang jelas (persen, bukan p), karena nama itulah yang muncul di petunjuk editor. Untuk penjelasan tambahan, tulis komentar JSDoc /** ... */ di atas function; editor juga menampilkannya saat hover.

    void vs undefined sebagai return

    void artinya "jangan andalkan nilai kembaliannya". undefined artinya "function ini benar-benar mengembalikan undefined". Untuk function biasa tanpa return, pakai void.

    Contoh kode

    // kasir.ts, function bertipe untuk aplikasi kasir sederhana
    type Item = { nama: string; harga: number; jumlah: number };
    
    // 1. Parameter + return eksplisit
    function subtotal(item: Item): number {
      return item.harga * item.jumlah;
    }
    
    // 2. Parameter opsional + default
    function hitungTotal(items: Item[], diskonPersen = 0, kodeVoucher?: string): number {
      let total = items.reduce((acc, item) => acc + subtotal(item), 0);
      total -= (total * diskonPersen) / 100;
      if (kodeVoucher === "HEMAT10K") total -= 10000;
      return Math.max(total, 0);
    }
    
    // 3. Callback type
    function cetakStruk(items: Item[], format: (angka: number) => string): void {
      for (const item of items) {
        console.log(`${item.nama} x${item.jumlah} = ${format(subtotal(item))}`);
      }
    }
    
    const rupiah = (angka: number): string => `Rp${angka.toLocaleString("id-ID")}`;
    
    const belanja: Item[] = [
      { nama: "Kopi", harga: 25000, jumlah: 2 },
      { nama: "Donat", harga: 12000, jumlah: 3 },
    ];
    
    cetakStruk(belanja, rupiah);
    // → Kopi x2 = Rp50.000
    // → Donat x3 = Rp36.000
    console.log(rupiah(hitungTotal(belanja)));                  // → Rp86.000
    console.log(rupiah(hitungTotal(belanja, 10)));              // → Rp77.400
    console.log(rupiah(hitungTotal(belanja, 0, "HEMAT10K")));   // → Rp76.000
    
    // 4. Kesalahan yang ditangkap TypeScript
    hitungTotal(belanja, "10"); // ❌ error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
    cetakStruk(belanja); // ❌ error TS2554: Expected 2 arguments, but got 1.
    

    Contoh overload dan bahaya lupa return:

    // overload.ts
    function ambilElemen(id: string): HTMLElement | null;
    function ambilElemen(ids: string[]): (HTMLElement | null)[];
    function ambilElemen(input: string | string[]): HTMLElement | null | (HTMLElement | null)[] {
      if (Array.isArray(input)) return input.map((id) => document.getElementById(id));
      return document.getElementById(input);
    }
    
    const satu = ambilElemen("judul");             // tipe: HTMLElement | null
    const banyak = ambilElemen(["judul", "isi"]);  // tipe: (HTMLElement | null)[]
    console.log(satu, banyak.length);
    
    // Lupa return di salah satu cabang
    function kategoriUmur(umur: number): string { // ❌ error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
      if (umur < 13) return "anak";
      if (umur < 18) return "remaja";
    }
    

    Error terakhir hanya muncul karena kita menulis : string sebagai return type. Tanpa anotasi itu, TypeScript diam-diam menyimpulkan string | undefined dan bug baru ketahuan saat ada yang memanggil kategoriUmur(30).toUpperCase().

    Kesalahan umum

    1. Tidak memberi tipe pada parameter. Di mode strict hasilnya error TS7006: Parameter 'x' implicitly has an 'any' type. Selalu beri tipe parameter, kecuali callback yang sudah mendapat contextual typing.
    2. Meletakkan parameter opsional sebelum yang wajib. function f(a?: string, b: number) adalah error. Pindahkan yang opsional ke belakang, atau pakai parameter object.
    3. Memakai Function sebagai tipe callback. Function menerima function apa saja tanpa info parameter. Tulis bentuknya: (id: number) => void.
    4. Tidak menulis return type di function publik. Bug "lupa return di satu cabang" lolos. Untuk function yang diekspor, tulis return type.
    5. Overload untuk segala hal. Kalau tipe kembalian tidak bergantung pada input, cukup pakai union sederhana. Overload menambah kerumitan yang harus dirawat.

    Latihan

    1. Mudah, Tulis luasLingkaran(jari: number): number dan kelilingLingkaran sebagai arrow function dengan tipe type Rumus = (jari: number) => number.
    2. Sedang, Tulis buatSlug(judul: string, pemisah = "-"): string yang mengubah "Belajar TypeScript Dasar" menjadi "belajar-typescript-dasar". Lalu tulis filterAngka(angka: number[], syarat: (n: number) => boolean): number[].
    3. Menantang, Tulis function dengan overload format(nilai: number): string (format rupiah) dan format(nilai: Date): string (format tanggal Indonesia, misalnya 24 Oktober 2026).
    Lihat solusi
    // 1. Mudah
    function luasLingkaran(jari: number): number {
      return Math.PI * jari ** 2;
    }
    type Rumus = (jari: number) => number;
    const kelilingLingkaran: Rumus = (jari) => 2 * Math.PI * jari;
    console.log(luasLingkaran(7).toFixed(2), kelilingLingkaran(7).toFixed(2)); // → 153.94 43.98
    
    // 2. Sedang
    function buatSlug(judul: string, pemisah = "-"): string {
      return judul.trim().toLowerCase().split(/\s+/).join(pemisah);
    }
    console.log(buatSlug("Belajar TypeScript Dasar")); // → belajar-typescript-dasar
    
    function filterAngka(angka: number[], syarat: (n: number) => boolean): number[] {
      return angka.filter(syarat);
    }
    console.log(filterAngka([1, 2, 3, 4, 5, 6], (n) => n % 2 === 0)); // → [ 2, 4, 6 ]
    
    // 3. Menantang
    function format(nilai: number): string;
    function format(nilai: Date): string;
    function format(nilai: number | Date): string {
      if (nilai instanceof Date) {
        return nilai.toLocaleDateString("id-ID", { day: "numeric", month: "long", year: "numeric" });
      }
      return `Rp${nilai.toLocaleString("id-ID")}`;
    }
    console.log(format(150000));                 // → Rp150.000
    console.log(format(new Date(2026, 9, 24)));  // → 24 Oktober 2026
    

    Ringkasan

    Video pendamping

    Kalau penjelasan di atas belum masuk, tonton ini. Video dari kanal YouTube pihak lain.

    Typescript Functions | Basics Tutorial for BeginnersDave Gray · Bahasa InggrisBuka di YouTube

    Kuis pemahaman

    Pilih satu jawaban per soal. Penjelasan muncul setelah kamu menjawab.