Wednesday

18-06-2025 Vol 19

“Record” utility type in TypeScript

Memahami Utility Type `Record` di TypeScript: Panduan Mendalam

TypeScript menyediakan serangkaian utility type yang ampuh untuk manipulasi tipe data. Salah satunya adalah `Record`, yang sangat berguna untuk mendefinisikan tipe objek dengan struktur kunci dan nilai yang konsisten. Artikel ini akan membahas secara mendalam utility type `Record`, bagaimana cara kerjanya, dan bagaimana Anda dapat menggunakannya untuk meningkatkan kode TypeScript Anda. Kita juga akan membahas praktik terbaik SEO untuk memastikan visibilitas artikel ini.

Daftar Isi

  1. Pendahuluan: Apa Itu Utility Type `Record`?
  2. Sintaks Dasar `Record`
  3. Memahami Parameter Tipe `Record`
    1. Kunci: Tipe Kunci Objek
    2. Nilai: Tipe Nilai Objek
  4. Contoh Penggunaan `Record`
    1. Mendefinisikan Objek dengan Kunci String dan Nilai Number
    2. Mendefinisikan Objek dengan Kunci String dan Nilai Tipe Custom
    3. Mendefinisikan Objek dengan Kunci Enum dan Nilai String
    4. Menggunakan `Record` dengan Tipe Union
    5. Menggunakan `Record` dengan Tipe Generik
  5. Kapan Menggunakan `Record`?
    1. Konsistensi Struktur Objek
    2. Kemudahan Penggunaan Tipe Data yang Dapat Diprediksi
    3. Mengurangi Duplikasi Tipe
  6. Perbandingan `Record` dengan Tipe Objek Lainnya
    1. `Record` vs. Interface
    2. `Record` vs. Type Alias
    3. `Record` vs. Mapped Types
  7. Keuntungan Menggunakan `Record`
    1. Keamanan Tipe yang Lebih Baik
    2. Kode yang Lebih Mudah Dibaca dan Dipelihara
    3. Peningkatan Produktivitas
  8. Keterbatasan `Record`
  9. Praktik Terbaik dalam Menggunakan `Record`
    1. Pilih Tipe Kunci yang Sesuai
    2. Gunakan Tipe Nilai yang Akurat
    3. Pertimbangkan Penggunaan Tipe Union dan Generik
  10. Contoh Kode Lanjutan dengan `Record`
    1. Validasi Data dengan `Record`
    2. Konversi Data dengan `Record`
    3. Manajemen Konfigurasi dengan `Record`
  11. Meningkatkan Keterbacaan Kode dengan `Record`
  12. Debugging dan Troubleshooting dengan `Record`
    1. Masalah Tipe yang Tidak Sesuai
    2. Kesalahan `undefined` atau `null`
  13. Integrasi `Record` dengan Library dan Framework Populer
    1. `Record` dengan React
    2. `Record` dengan Angular
    3. `Record` dengan Vue.js
    4. `Record` dengan Node.js
  14. Studi Kasus Nyata Penggunaan `Record`
  15. Kesimpulan: Mengapa `Record` Penting dalam TypeScript
  16. Referensi dan Sumber Daya Tambahan

1. Pendahuluan: Apa Itu Utility Type `Record`?

Utility type `Record` adalah salah satu fitur yang paling berguna di TypeScript. Utility type ini memungkinkan Anda untuk mendefinisikan sebuah tipe objek di mana semua kunci memiliki tipe yang sama, dan semua nilai memiliki tipe yang sama pula. Ini sangat berguna ketika Anda perlu memastikan bahwa objek Anda memiliki struktur yang konsisten.

Singkatnya, `Record` membantu Anda membuat definisi tipe yang kuat dan dapat diprediksi untuk objek, yang mengurangi kesalahan dan meningkatkan kualitas kode secara keseluruhan.

2. Sintaks Dasar `Record`

Sintaks `Record` cukup sederhana. Anda mendefinisikannya dengan menggunakan kata kunci `Record` diikuti oleh dua parameter tipe dalam tanda kurung sudut (`< >`):

Record<Keys, Type>

Di sini:

  • Keys adalah tipe kunci objek. Ini bisa berupa string literal, tipe union string, enum, atau tipe lain yang dapat digunakan sebagai kunci objek.
  • Type adalah tipe nilai objek. Ini bisa berupa tipe apa pun, termasuk tipe primitif, tipe custom, atau bahkan utility type lainnya.

3. Memahami Parameter Tipe `Record`

Mari kita gali lebih dalam kedua parameter tipe yang digunakan dalam `Record`:

3.1. Kunci: Tipe Kunci Objek

Tipe kunci menentukan jenis kunci yang dapat digunakan dalam objek Anda. Beberapa contoh tipe kunci yang umum meliputi:

  • String Literal: Kunci tetap yang ditentukan secara eksplisit, misalnya "nama", "usia".
  • Tipe Union String: Gabungan beberapa string literal, misalnya "nama" | "usia" | "alamat".
  • Enum: Kumpulan konstanta bernama, yang sering digunakan untuk mewakili pilihan atau opsi.
  • string atau number atau symbol: Tipe generik yang memungkinkan kunci bertipe string, number, atau symbol apa pun.

3.2. Nilai: Tipe Nilai Objek

Tipe nilai menentukan jenis nilai yang dapat disimpan dalam objek Anda. Ini bisa berupa tipe TypeScript apa pun, termasuk:

  • Tipe Primitif: string, number, boolean, null, undefined, symbol, bigint.
  • Tipe Custom: Interface, type alias, class.
  • Tipe Union: Kombinasi beberapa tipe, misalnya string | number.
  • Tipe Generik: Tipe yang diparameterisasi oleh tipe lain, misalnya Array<T>.
  • Utility Type Lainnya: Misalnya, `Partial`, `Readonly`, `Pick`, dll.

4. Contoh Penggunaan `Record`

Berikut adalah beberapa contoh cara penggunaan utility type `Record` dalam praktiknya:

4.1. Mendefinisikan Objek dengan Kunci String dan Nilai Number

Contoh ini mendefinisikan objek di mana kunci adalah string dan nilai adalah angka (number):


type NilaiUjian = Record<string, number>;

const nilai: NilaiUjian = {
  matematika: 90,
  fisika: 85,
  kimia: 92,
};

console.log(nilai.matematika); // Output: 90

Dalam contoh ini, `NilaiUjian` adalah tipe yang mendefinisikan objek yang memiliki kunci string (misalnya, “matematika”, “fisika”, “kimia”) dan nilai numerik (misalnya, 90, 85, 92). Jika Anda mencoba menambahkan kunci dengan tipe selain string atau nilai dengan tipe selain angka, TypeScript akan memberikan kesalahan kompilasi.

4.2. Mendefinisikan Objek dengan Kunci String dan Nilai Tipe Custom

Anda juga dapat menggunakan `Record` untuk mendefinisikan objek di mana nilai memiliki tipe custom. Misalnya:


interface Pengguna {
  nama: string;
  usia: number;
  email: string;
}

type DaftarPengguna = Record<string, Pengguna>;

const daftar: DaftarPengguna = {
  user1: { nama: "John Doe", usia: 30, email: "john.doe@example.com" },
  user2: { nama: "Jane Smith", usia: 25, email: "jane.smith@example.com" },
};

console.log(daftar.user1.nama); // Output: John Doe

Di sini, `Pengguna` adalah sebuah interface yang mendefinisikan struktur data pengguna. `DaftarPengguna` adalah tipe `Record` yang mendefinisikan objek yang memiliki kunci string dan nilai tipe `Pengguna`. Ini memungkinkan Anda untuk membuat objek yang berisi daftar pengguna dengan kunci unik.

4.3. Mendefinisikan Objek dengan Kunci Enum dan Nilai String

Penggunaan lain yang umum adalah mendefinisikan objek dengan kunci enum. Ini berguna ketika Anda ingin memetakan nilai enum ke string atau deskripsi yang sesuai:


enum StatusPesanan {
  Dibuat = "DIBUAT",
  Diproses = "DIPROSES",
  Dikirim = "DIKIRIM",
  Selesai = "SELESAI",
}

type DeskripsiStatus = Record<StatusPesanan, string>;

const deskripsi: DeskripsiStatus = {
  [StatusPesanan.Dibuat]: "Pesanan baru telah dibuat.",
  [StatusPesanan.Diproses]: "Pesanan sedang diproses.",
  [StatusPesanan.Dikirim]: "Pesanan telah dikirim.",
  [StatusPesanan.Selesai]: "Pesanan telah selesai.",
};

console.log(deskripsi[StatusPesanan.Diproses]); // Output: Pesanan sedang diproses.

Dalam contoh ini, `StatusPesanan` adalah enum yang mewakili status pesanan yang berbeda. `DeskripsiStatus` adalah tipe `Record` yang memetakan setiap nilai enum ke deskripsi string yang sesuai. Ini membantu Anda menjaga konsistensi dan keterbacaan kode Anda.

4.4. Menggunakan `Record` dengan Tipe Union

Anda dapat menggunakan tipe union sebagai tipe kunci di `Record` untuk membatasi kunci yang diperbolehkan:


type OpsiWarna = "merah" | "hijau" | "biru";

type KodeWarna = Record<OpsiWarna, string>;

const kode: KodeWarna = {
  merah: "#FF0000",
  hijau: "#00FF00",
  biru: "#0000FF",
};

console.log(kode.merah); // Output: #FF0000

Dalam contoh ini, `OpsiWarna` adalah tipe union yang menentukan tiga opsi warna yang mungkin. `KodeWarna` adalah tipe `Record` yang mendefinisikan objek yang hanya dapat memiliki kunci dari tipe `OpsiWarna` dan nilai string.

4.5. Menggunakan `Record` dengan Tipe Generik

`Record` juga dapat digunakan dengan tipe generik untuk membuat tipe yang lebih fleksibel. Misalnya:


type ObjekDenganProperti<T> = Record<string, T>;

const dataString: ObjekDenganProperti<string> = {
  nama: "John Doe",
  alamat: "Jl. Contoh No. 123",
};

const dataNumber: ObjekDenganProperti<number> = {
  usia: 30,
  tinggi: 175,
};

console.log(dataString.nama); // Output: John Doe
console.log(dataNumber.usia); // Output: 30

Di sini, `ObjekDenganProperti` adalah tipe generik yang menerima parameter tipe `T`, yang menentukan tipe nilai objek. Ini memungkinkan Anda untuk membuat objek dengan kunci string dan nilai tipe apa pun yang Anda tentukan.

5. Kapan Menggunakan `Record`?

Utility type `Record` sangat berguna dalam situasi berikut:

5.1. Konsistensi Struktur Objek

Ketika Anda perlu memastikan bahwa semua objek dalam suatu koleksi memiliki struktur yang sama, `Record` sangat membantu. Ini mencegah kesalahan yang disebabkan oleh properti yang hilang atau salah tipe.

5.2. Kemudahan Penggunaan Tipe Data yang Dapat Diprediksi

Dengan `Record`, Anda tahu persis tipe data apa yang diharapkan untuk setiap kunci dan nilai. Ini membuat kode Anda lebih mudah dibaca, dipahami, dan dipelihara.

5.3. Mengurangi Duplikasi Tipe

Daripada mendefinisikan tipe objek secara manual berulang kali, Anda dapat menggunakan `Record` untuk membuat tipe yang reusable dan konsisten.

6. Perbandingan `Record` dengan Tipe Objek Lainnya

TypeScript menawarkan beberapa cara untuk mendefinisikan tipe objek. Mari kita bandingkan `Record` dengan beberapa opsi lainnya:

6.1. `Record` vs. Interface

Interface: Interface digunakan untuk mendefinisikan kontrak yang menggambarkan struktur objek. Interface mendukung pewarisan dan implementasi.
`Record`: `Record` lebih cocok untuk mendefinisikan tipe objek dengan kunci dan nilai yang seragam.

Contoh Interface:


interface Point {
  x: number;
  y: number;
}

Contoh `Record`:


type PointRecord = Record<"x" | "y", number>;

Dalam contoh ini, Interface `Point` dan `Record` `PointRecord` dapat digunakan secara bergantian, namun Interface lebih fleksibel untuk skenario yang lebih kompleks.

6.2. `Record` vs. Type Alias

Type Alias: Type alias hanya memberi nama lain untuk tipe yang sudah ada.
`Record`: `Record` sebenarnya membuat tipe objek baru berdasarkan parameter yang diberikan.

Contoh Type Alias:


type StringOrNumber = string | number;

Contoh `Record`:


type StringNumberMap = Record<string, number>;

Type alias hanya membuat alias untuk `string | number`, sementara `Record` membuat tipe objek baru dengan kunci string dan nilai number.

6.3. `Record` vs. Mapped Types

Mapped Types: Mapped types memungkinkan Anda untuk membuat tipe baru dengan mengubah properti tipe yang sudah ada.
`Record`: `Record` lebih sederhana dan berfokus pada pembuatan tipe objek dengan kunci dan nilai yang seragam. Mapped Types memberikan fleksibilitas yang lebih besar dalam memodifikasi tipe properti.

Contoh Mapped Types:


interface Person {
  nama: string;
  usia: number;
}

type ReadonlyPerson = Readonly<Person>;

Contoh `Record`:


type StringBooleanMap = Record<string, boolean>;

Mapped types memungkinkan Anda membuat tipe `ReadonlyPerson` di mana semua properti dari `Person` menjadi read-only. `Record`, di sisi lain, membuat tipe objek baru dengan kunci string dan nilai boolean.

7. Keuntungan Menggunakan `Record`

Penggunaan utility type `Record` memberikan beberapa keuntungan:

7.1. Keamanan Tipe yang Lebih Baik

Dengan mendefinisikan struktur objek secara eksplisit, Anda meminimalkan risiko kesalahan tipe runtime. TypeScript akan menangkap kesalahan ini selama proses kompilasi.

7.2. Kode yang Lebih Mudah Dibaca dan Dipelihara

Tipe yang didefinisikan dengan baik membuat kode Anda lebih mudah dipahami oleh pengembang lain (dan diri Anda di masa depan). Ini juga mempermudah pemeliharaan dan refactoring kode.

7.3. Peningkatan Produktivitas

Dengan memanfaatkan `Record`, Anda dapat menulis kode yang lebih cepat dan lebih efisien. Anda tidak perlu khawatir tentang tipe data yang salah, karena TypeScript akan membantu Anda.

8. Keterbatasan `Record`

Meskipun `Record` adalah utility type yang berguna, ia memiliki beberapa keterbatasan:

  • `Record` mengharuskan semua kunci memiliki tipe yang sama. Jika Anda perlu membuat objek dengan kunci tipe yang berbeda, Anda mungkin perlu menggunakan interface atau type alias.
  • `Record` tidak mendukung pewarisan atau implementasi seperti interface. Jika Anda membutuhkan fitur-fitur ini, interface mungkin lebih cocok.

9. Praktik Terbaik dalam Menggunakan `Record`

Berikut adalah beberapa praktik terbaik untuk menggunakan utility type `Record` secara efektif:

9.1. Pilih Tipe Kunci yang Sesuai

Pilih tipe kunci yang paling sesuai dengan kebutuhan Anda. Jika Anda tahu kunci spesifik yang akan digunakan, gunakan string literal atau tipe union string. Jika Anda membutuhkan fleksibilitas yang lebih besar, gunakan tipe `string` atau `number`.

9.2. Gunakan Tipe Nilai yang Akurat

Tentukan tipe nilai yang paling akurat yang mungkin. Ini akan membantu Anda mencegah kesalahan tipe dan meningkatkan keamanan kode Anda.

9.3. Pertimbangkan Penggunaan Tipe Union dan Generik

Gunakan tipe union dan generik untuk membuat tipe `Record` yang lebih fleksibel dan reusable.

10. Contoh Kode Lanjutan dengan `Record`

Berikut adalah beberapa contoh kode lanjutan yang menunjukkan bagaimana Anda dapat menggunakan `Record` dalam skenario dunia nyata:

10.1. Validasi Data dengan `Record`


interface Konfigurasi {
  apiUrl: string;
  timeout: number;
  debug: boolean;
}

type KonfigurasiValidator = Record<keyof Konfigurasi, (value: any) => boolean>;

const validator: KonfigurasiValidator = {
  apiUrl: (value) => typeof value === "string" && value.startsWith("https://"),
  timeout: (value) => typeof value === "number" && value > 0,
  debug: (value) => typeof value === "boolean",
};

function validasiKonfigurasi(konfigurasi: Konfigurasi): boolean {
  for (const key in konfigurasi) {
    if (validator[key] && !validator[key](konfigurasi[key])) {
      console.error(`Validasi gagal untuk ${key}: ${konfigurasi[key]}`);
      return false;
    }
  }
  return true;
}

const konfigurasiValid: Konfigurasi = {
  apiUrl: "https://api.example.com",
  timeout: 1000,
  debug: true,
};

const konfigurasiTidakValid: Konfigurasi = {
  apiUrl: "http://api.example.com", // Tidak valid karena bukan HTTPS
  timeout: -100, // Tidak valid karena negatif
  debug: "salah", // Tidak valid karena bukan boolean
} as any;

console.log("Konfigurasi Valid:", validasiKonfigurasi(konfigurasiValid));
console.log("Konfigurasi Tidak Valid:", validasiKonfigurasi(konfigurasiTidakValid));

Dalam contoh ini, kita menggunakan `Record` untuk mendefinisikan validator untuk setiap properti dalam interface `Konfigurasi`. Ini memungkinkan kita untuk memastikan bahwa data konfigurasi valid sebelum digunakan.

10.2. Konversi Data dengan `Record`


enum MataUang {
  USD = "USD",
  EUR = "EUR",
  GBP = "GBP",
}

type KursMataUang = Record<MataUang, number>;

const kurs: KursMataUang = {
  [MataUang.USD]: 1.0,
  [MataUang.EUR]: 0.85,
  [MataUang.GBP]: 0.75,
};

function konversiMataUang(jumlah: number, dari: MataUang, ke: MataUang): number {
  const kursDari = kurs[dari];
  const kursKe = kurs[ke];
  if (!kursDari || !kursKe) {
    throw new Error("Mata uang tidak didukung.");
  }
  return jumlah * (kursKe / kursDari);
}

console.log(konversiMataUang(100, MataUang.USD, MataUang.EUR)); // Output: 85

Dalam contoh ini, kita menggunakan `Record` untuk menyimpan kurs mata uang yang berbeda. Ini memungkinkan kita untuk mengkonversi jumlah uang dari satu mata uang ke mata uang lain dengan mudah.

10.3. Manajemen Konfigurasi dengan `Record`


interface FiturBendera {
  fiturA: boolean;
  fiturB: boolean;
  fiturC: boolean;
}

type KonfigurasiDefault = Record<keyof FiturBendera, boolean>;

const konfigurasiDefault: KonfigurasiDefault = {
  fiturA: false,
  fiturB: true,
  fiturC: false,
};

function dapatkanKonfigurasi(): FiturBendera {
  // Di sini Anda bisa membaca konfigurasi dari file, database, dll.
  return { ...konfigurasiDefault };
}

const konfigurasiSaatIni = dapatkanKonfigurasi();

console.log("Fitur A aktif:", konfigurasiSaatIni.fiturA);
console.log("Fitur B aktif:", konfigurasiSaatIni.fiturB);
console.log("Fitur C aktif:", konfigurasiSaatIni.fiturC);

Dalam contoh ini, kita menggunakan `Record` untuk mendefinisikan konfigurasi default untuk fitur bendera yang berbeda. Ini memungkinkan kita untuk mengelola fitur bendera dengan mudah dan memastikan bahwa semua fitur memiliki nilai default.

11. Meningkatkan Keterbacaan Kode dengan `Record`

Penggunaan `Record` dapat meningkatkan keterbacaan kode secara signifikan. Dengan mendefinisikan struktur objek secara eksplisit, Anda membuat kode Anda lebih mudah dipahami dan dipelihara. Contohnya, daripada memiliki objek dengan properti yang mungkin tidak jelas tipenya, `Record` memaksa Anda untuk mendefinisikannya, yang membuat kode Anda lebih jelas bagi pengembang lain (dan diri Anda di masa depan).

12. Debugging dan Troubleshooting dengan `Record`

Meskipun `Record` membantu mencegah kesalahan, Anda mungkin masih mengalami masalah saat menggunakannya. Berikut adalah beberapa masalah umum dan cara menanganinya:

12.1. Masalah Tipe yang Tidak Sesuai

Jika Anda mencoba menetapkan nilai dengan tipe yang salah ke properti `Record`, TypeScript akan memberikan kesalahan. Periksa kembali definisi tipe `Record` Anda dan pastikan bahwa Anda menetapkan nilai dengan tipe yang benar.

12.2. Kesalahan `undefined` atau `null`

Jika Anda mencoba mengakses properti yang tidak ada di objek `Record`, Anda mungkin mendapatkan kesalahan `undefined`. Pastikan bahwa semua properti yang Anda akses ada dan didefinisikan.

13. Integrasi `Record` dengan Library dan Framework Populer

Utility type `Record` dapat diintegrasikan dengan berbagai library dan framework populer untuk meningkatkan keamanan dan keterbacaan kode Anda.

13.1. `Record` dengan React

Anda dapat menggunakan `Record` untuk mendefinisikan tipe props untuk komponen React. Ini membantu Anda memastikan bahwa komponen Anda menerima props dengan tipe yang benar.


import React from 'react';

interface Props {
  data: Record<string, any>;
}

const MyComponent: React.FC<Props> = ({ data }) => {
  return (
    <div>
      {Object.entries(data).map(([key, value]) => (
        <p key={key}>{key}: {String(value)}</p>
      ))}
    </div>
  );
};

export default MyComponent;

13.2. `Record` dengan Angular

Anda dapat menggunakan `Record` untuk mendefinisikan tipe data untuk layanan Angular atau untuk model data Anda. Ini membantu Anda memastikan bahwa data Anda memiliki struktur yang konsisten.


import { Injectable } from '@angular/core';

type UserData = Record<string, any>;

@Injectable({
  providedIn: 'root'
})
export class UserService {
  getUserData(): UserData {
    return {
      name: 'John Doe',
      email: 'john.doe@example.com'
    };
  }
}

13.3. `Record` dengan Vue.js

Anda dapat menggunakan `Record` untuk mendefinisikan tipe props untuk komponen Vue.js atau untuk data yang disimpan dalam Vuex store. Ini membantu Anda memastikan bahwa data Anda memiliki tipe yang benar.


<template>
  <div>
    <p v-for="(value, key) in data" :key="key">{{ key }}: {{ value }}</p>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  props: {
    data: {
      type: Object as () => Record<string, any>,
      required: true
    }
  }
});
</script>

13.4. `Record` dengan Node.js

Anda dapat menggunakan `Record` untuk mendefinisikan tipe data untuk konfigurasi aplikasi Node.js Anda atau untuk data yang diterima dari API eksternal.


interface AppConfig {
  port: number;
  apiUrl: string;
  databaseUrl: string;
}

type Config = Record<keyof AppConfig, string | number>;

const config: Config = {
  port: 3000,
  apiUrl: 'https://api.example.com',
  databaseUrl: 'mongodb://localhost:27017/mydb'
};

console.log(`App listening on port ${config.port}`);

14. Studi Kasus Nyata Penggunaan `Record`

Mari kita lihat beberapa studi kasus nyata di mana `Record` sangat berguna:

  • Sistem Manajemen Konfigurasi: `Record` dapat digunakan untuk mendefinisikan struktur file konfigurasi, memastikan bahwa semua konfigurasi memiliki tipe yang benar dan nilai default.
  • Aplikasi E-commerce: `Record` dapat digunakan untuk mendefinisikan struktur data produk, kategori, dan pesanan, memastikan bahwa semua data memiliki struktur yang konsisten.
  • Dashboard Analitik: `Record` dapat digunakan untuk mendefinisikan struktur data metrik dan laporan, memastikan bahwa semua data memiliki tipe yang benar dan format yang konsisten.

15. Kesimpulan: Mengapa `Record` Penting dalam TypeScript

Utility type `Record` adalah alat yang ampuh dalam TypeScript yang memungkinkan Anda untuk mendefinisikan tipe objek dengan struktur kunci dan nilai yang konsisten. Ini membantu Anda meningkatkan keamanan tipe, keterbacaan kode, dan produktivitas. Meskipun memiliki beberapa keterbatasan, `Record` adalah tambahan yang berharga untuk toolkit TypeScript Anda dan harus digunakan secara luas untuk membuat kode yang lebih robust dan mudah dipelihara.

16. Referensi dan Sumber Daya Tambahan

“`

omcoding

Leave a Reply

Your email address will not be published. Required fields are marked *