utility-typesfunctionMenengah4 mnt baca

ReturnType, Parameters, dan Awaited

Ambil tipe return dan argumen fungsi pihak ketiga tanpa menulis ulang, plus membuka Promise dengan Awaited.

Kenapa Utility Ini Ada?

Di project nyata kamu sering memakai fungsi library atau modul lain yang tipe return-nya kompleks: object bertingkat, hasil query, response API. Menyalin bentuknya manual ke interface sendiri berarti setiap update library bisa membuat salinanmu basi tanpa ada yang memberitahu. ReturnType<T>, Parameters<T>, dan Awaited<T> mengambil tipe LANGSUNG dari fungsi sumbernya, jadi tipe turunanmu selalu sinkron dengan implementasi aslinya. Ubah return function-nya, semua yang memakai tipenya ikut berubah dan compiler menunjukkan semua tempat yang terdampak.

ReturnType: Tipe Return Tanpa Memanggil Fungsi

ReturnType<T> mengambil tipe dari nilai yang dikembalikan fungsi bertipe T. Karena ia butuh TIPE fungsi (bukan nilainya), pasangannya adalah typeof yang mengambil tipe dari sebuah nilai:

typescript
// Fungsi dari library atau modul lain, return-nya kompleks
function formatRupiah(nilai: number): { teks: string; mentah: number } {
  return { teks: `Rp${nilai.toLocaleString("id-ID")}`, mentah: nilai };
}

type HasilFormat = ReturnType<typeof formatRupiah>;
// { teks: string; mentah: number }

function tampilkanHarga(n: number, fmt: (x: number) => HasilFormat): string {
  return fmt(n).teks;
}

tampilkanHarga(50000, formatRupiah); // OK, signature cocok otomatis

Tanpa ReturnType, kamu menulis ulang bentuk return di setiap pemakaian. Dengan ReturnType<typeof formatRupiah>, fungsi itu sendiri adalah satu-satunya sumber kebenaran.

Parameters: Tipe Argumen sebagai Tuple

Parameters<T> mengambil tipe parameter fungsi sebagai tuple, sehingga kamu bisa meneruskannya, menyimpannya, atau melakukan spread dengan aman:

typescript
function buatPesanan(id: string, jumlah: number, prioritas?: boolean): void {
  console.log(id, jumlah, prioritas);
}

type ArgumenPesanan = Parameters<typeof buatPesanan>;
// [id: string, jumlah: number, prioritas?: boolean | undefined]

const args: ArgumenPesanan = ["P-1", 3];
buatPesanan(...args); // OK, spread tuple tetap type-safe

// Pola wrapper generik: bungkus fungsi apa pun tanpa tahu signature-nya
function denganLog<T extends (...a: never[]) => unknown>(fn: T) {
  return (...args: Parameters<T>): ReturnType<T> => {
    console.log("dipanggil dengan:", args);
    return fn(...args);
  };
}

const pesananBerlog = denganLog(buatPesanan);
pesananBerlog("P-2", 5, true); // signature asli tetap terjaga

Pasangan Parameters<T> + ReturnType<T> adalah fondasi pola wrapper: tambah logging, caching, atau retry tanpa mengulang signature di mana pun.

Awaited: Membuka Promise

Fungsi async selalu mengembalikan Promise<...>, tapi yang kamu butuhkan biasanya adalah tipe di DALAM promise-nya. Awaited<T> membuka lapisan Promise secara rekursif:

typescript
async function ambilUser(id: string): Promise<{ id: string; nama: string }> {
  return { id, nama: "Budi" };
}

type User = Awaited<ReturnType<typeof ambilUser>>;
// { id: string; nama: string }  (bukan Promise<...>)

async function proses(id: string): Promise<void> {
  const u: User = await ambilUser(id); // tipe cocok tanpa Promise
  console.log(u.nama);
}

// Awaited membuka berlapis-lapis sekaligus
type Dalam = Awaited<Promise<Promise<number>>>; // number

Idiom Awaited<ReturnType<typeof fn>> memberi "tipe data sebenarnya" dari fungsi async milik orang lain dalam satu baris.

Contoh Error Nyata: Dipakai ke Bukan Fungsi

Ketiga utility ini mensyaratkan tipe fungsi. Memakainya ke tipe lain menghasilkan error constraint yang cukup jelas:

typescript
const nama = "Budi";

type Salah1 = ReturnType<typeof nama>;
// Error: Type 'string' does not satisfy the constraint '(...args: any) => any'.

type Salah2 = Parameters<number>;
// Error: Type 'number' does not satisfy the constraint '(...args: any) => any'.

Pesan does not satisfy the constraint memberitahu syarat yang gagal: argumen harus bertipe fungsi. Penyebab paling sering adalah lupa typeof: ReturnType menerima tipe, sedangkan formatRupiah tanpa typeof adalah nilai. Aturan praktis: bungkus nilai fungsi dengan typeof dulu sebelum masuk ke ReturnType atau Parameters.

Jebakan kedua: pada fungsi overload, ReturnType<typeof fn> hanya mengambil signature overload TERAKHIR. Kalau wrapper-mu aneh pada fungsi overload, itu penyebabnya.

Catatan teknis: ReturnType<F> didefinisikan kira-kira sebagai F extends (...args: never[]) => infer R ? R : never: kata kunci infer "menangkap" tipe return dari signature. Ketiga utility ini dibangun di atas conditional type + infer, jembatan alami menuju modul conditional types.

Tantangan

Wrapper async yang type-safe

Diberi declare function hitungDiskon(harga: number, persen: number): Promise<number>. Definisikan TipeHasil = Awaited<ReturnType<typeof hitungDiskon>> dan Argumen = Parameters<typeof hitungDiskon>. Lalu tulis fungsi async terapkanDiskon(...args: Argumen): Promise<TipeHasil> yang memanggil hitungDiskon dan mengembalikan hasilnya. Buktikan tipe cocok dengan satu pemanggilan contoh.