Skip to content

Mengenal Utility Types di TypeScript Cara Menggunakan dan Contoh Praktis

Published: at 19.00

Catatan lanjutan dari kelas be10x kemarin

Daftar Isi

  1. Apa Itu Utility Types?
  2. Partial<T>
  3. Required<T>
  4. Readonly<T>
  5. Pick<T, K>
  6. Omit<T, K>
  7. Record<K, T>
  8. Exclude<T, U>
  9. Extract<T, U>
  10. NonNullable<T>
  11. InstanceType<T>
  12. Contoh Kasus Dunia Nyata
  13. Praktik Terbaik dalam Menggunakan Utility Types
  14. Kesimpulan

Apa Itu Utility Types?

Utility Types adalah tipe-tipe bawaan di TypeScript yang memudahkan manipulasi tipe yang sudah ada untuk membuat tipe baru. Mereka membantu dalam pengembangan dengan membuat tipe lebih dinamis dan fleksibel tanpa mengorbankan keamanan tipe.

Mengapa Menggunakan Utility Types?

Pendapat Pribadi: Dalam pengalaman saya, Utility Types sangat membantu dalam mengelola tipe data yang kompleks, terutama dalam aplikasi skala besar.

Partial<T>

Partial<T> membuat semua properti dalam tipe T menjadi opsional.

Ilustrasi Partial<T>

Loading graph...

graph TD A[Type T] B["Partial<T>"] A -->|Semua properti wajib| A B -->|Semua properti opsional| B
graph TD
    A[Type T]
    B["Partial<T>"]
    A -->|Semua properti wajib| A
    B -->|Semua properti opsional| B

Penjelasan Ilustrasi:

Contoh 1: Menggunakan Partial<T>

interface User {
  id: number;
  name: string;
  email: string;
}

function updateUser(user: User, updates: Partial<User>): User {
  return { ...user, ...updates };
}

let user1: User = { id: 1, name: "Ibrahim", email: "[email protected]" };

let updatedUser = updateUser(user1, { email: "[email protected]" });

console.log(updatedUser);
// Output: { id: 1, name: "Ibrahim", email: "[email protected]" }

Penjelasan:

Required<T>

Required<T> membuat semua properti dalam tipe T menjadi wajib (non-opsional).

Ilustrasi Required<T>

Loading graph...

graph TD A[Type T dengan properti opsional] B["Required<T>"] A -->|Beberapa properti opsional| B B -->|Semua properti wajib| A
graph TD
    A[Type T dengan properti opsional]
    B["Required<T>"]
    A -->|Beberapa properti opsional| B
    B -->|Semua properti wajib| A

Penjelasan Ilustrasi:

Contoh 2: Menggunakan Required<T>

interface User {
  id: number;
  name?: string;
  email?: string;
}

let user2: Required<User> = {
  id: 2,
  name: "Ali",
  email: "[email protected]",
};

// Akan error jika properti name atau email tidak ada.

Pendapat Pribadi: Required<T> berguna ketika kita ingin memastikan bahwa semua properti harus ada, misalnya saat menyimpan data ke database.

Readonly<T>

Readonly<T> membuat semua properti dalam tipe T menjadi hanya-baca.

Ilustrasi Readonly<T>

Loading graph...

graph LR T[Type T: User] T -- properti dapat diubah --> "Readonly<T>" "Readonly<T>" -- properti menjadi hanya-baca --> T
graph LR
    T[Type T: User]
    T -- properti dapat diubah --> "Readonly<T>"
    "Readonly<T>" -- properti menjadi hanya-baca --> T

Penjelasan Ilustrasi:

Contoh 3: Menggunakan Readonly<T>

interface User {
  id: number;
  name: string;
}

let user3: Readonly<User> = {
  id: 3,
  name: "Fatimah",
};

// user3.name = "Aisyah"; // Error: Cannot assign to 'name' because it is a read-only property.

Catatan: Menggunakan Readonly<T> membantu mencegah perubahan yang tidak diinginkan pada objek, meningkatkan integritas data.

Pick<T, K>

Pick<T, K> membuat tipe baru dengan memilih subset properti K dari tipe T.

Ilustrasi Pick<T, K>

Loading graph...

graph LR T[Type T: User] K["Properties K: 'id', 'name'"] T -- pilih properti K --> "Pick<T, K>" "Pick<T, K>" -- hanya properti K --> T
graph LR
    T[Type T: User]
    K["Properties K: 'id', 'name'"]
    T -- pilih properti K --> "Pick<T, K>"
    "Pick<T, K>" -- hanya properti K --> T

Penjelasan Ilustrasi:

Contoh 4: Menggunakan Pick<T, K>

interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

type UserPreview = Pick<User, "id" | "name">;

let userPreview: UserPreview = {
  id: 4,
  name: "Zainab",
};

Penjelasan:

Omit<T, K>

Omit<T, K> membuat tipe baru dengan menghilangkan properti K dari tipe T.

Ilustrasi Omit<T, K>

Loading graph...

graph LR T[Type T: User] K["Properties K: 'password'"] T -- semua properti --> "Omit<T, K>" "Omit<T, K>" -- tanpa properti K --> T
graph LR
    T[Type T: User]
    K["Properties K: 'password'"]
    T -- semua properti --> "Omit<T, K>"
    "Omit<T, K>" -- tanpa properti K --> T

Penjelasan Ilustrasi:

Contoh 5: Menggunakan Omit<T, K>

type UserWithoutEmail = Omit<User, "email">;

let userWithoutEmail: UserWithoutEmail = {
  id: 5,
  name: "Umar",
  age: 28,
};

Penjelasan:

Record<K, T>

Record<K, T> membuat tipe objek dengan kunci tipe K dan nilai tipe T.

Ilustrasi Record<K, T>

Loading graph...

graph LR subgraph K [Keys K] A["admin"] B["user"] C["guest"] end T[Type T: Permissions] K -- menjadi kunci --> "Record<K, T>" T -- menjadi nilai --> "Record<K, T>"
graph LR
    subgraph K [Keys K]
        A["admin"]
        B["user"]
        C["guest"]
    end
    T[Type T: Permissions]
    K -- menjadi kunci --> "Record<K, T>"
    T -- menjadi nilai --> "Record<K, T>"

Penjelasan Ilustrasi:

Contoh 6: Menggunakan Record<K, T>

type Roles = "admin" | "user" | "guest";

interface Permissions {
  canRead: boolean;
  canWrite: boolean;
  canDelete: boolean;
}

let rolePermissions: Record<Roles, Permissions> = {
  admin: { canRead: true, canWrite: true, canDelete: true },
  user: { canRead: true, canWrite: true, canDelete: false },
  guest: { canRead: true, canWrite: false, canDelete: false },
};

Pendapat Pribadi: Record<K, T> sangat berguna untuk membuat objek map dengan tipe kunci dan nilai yang spesifik.

Exclude<T, U>

Exclude<T, U> membuat tipe dengan mengecualikan tipe U dari tipe T.

Ilustrasi Exclude<T, U>

Loading graph...

graph LR subgraph T [Set T] A["active"] B["inactive"] C["pending"] end subgraph U [Set U] A["active"] end subgraph "Exclude<T, U>" B["inactive"] C["pending"] end T -- dikurangi U --> "Exclude<T, U>"
graph LR
    subgraph T [Set T]
        A["active"]
        B["inactive"]
        C["pending"]
    end
    subgraph U [Set U]
        A["active"]
    end
    subgraph "Exclude<T, U>"
        B["inactive"]
        C["pending"]
    end
    T -- dikurangi U --> "Exclude<T, U>"

Penjelasan Ilustrasi:

Contoh 7: Menggunakan Exclude<T, U>

type Status = "active" | "inactive" | "pending";

type ActiveStatus = Exclude<Status, "inactive">;

// ActiveStatus hanya "active" atau "pending"

Extract<T, U>

Extract<T, U> membuat tipe dengan mengekstrak tipe yang ada di T dan U.

Ilustrasi Extract<T, U>

Loading graph...

graph TD A[Set T] B[Set U] C["Extract<T, U>"] A -->|Semua elemen T| A B -->|Elemen yang akan diekstrak| B A -.->|Mengambil elemen yang sama dengan U| C
graph TD
    A[Set T]
    B[Set U]
    C["Extract<T, U>"]
    A -->|Semua elemen T| A
    B -->|Elemen yang akan diekstrak| B
    A -.->|Mengambil elemen yang sama dengan U| C

Penjelasan Ilustrasi:

Contoh 8: Menggunakan Extract<T, U>

type Status = "active" | "inactive" | "pending";

type VisibleStatus = Extract<Status, "active" | "pending">;

// VisibleStatus adalah "active" atau "pending"

NonNullable<T>

NonNullable<T> membuat tipe dengan menghilangkan null dan undefined dari tipe T.

Ilustrasi NonNullable<T>

Loading graph...

graph TD A[Type T dengan null dan undefined] B["NonNullable<T>"] A -->|Menghilangkan null dan undefined| B
graph TD
    A[Type T dengan null dan undefined]
    B["NonNullable<T>"]
    A -->|Menghilangkan null dan undefined| B

Penjelasan Ilustrasi:

Contoh 9: Menggunakan NonNullable<T>

type Name = string | null | undefined;

type ValidName = NonNullable<Name>;

// ValidName hanya string, tidak termasuk null atau undefined

Catatan: Sangat berguna ketika kita ingin memastikan bahwa tipe tidak mengandung null atau undefined.

InstanceType<T>

InstanceType<T> mendapatkan tipe instance dari constructor atau class.

Ilustrasi InstanceType<T>

Loading graph...

graph TD A[Constructor T] B["InstanceType<T>"] A -->|Mendapatkan tipe instance dari T| B
graph TD
    A[Constructor T]
    B["InstanceType<T>"]
    A -->|Mendapatkan tipe instance dari T| B

Penjelasan Ilustrasi:

Contoh 10: Menggunakan InstanceType<T>

class Person {
  constructor(public name: string, public age: number) {}
}

type PersonInstance = InstanceType<typeof Person>;

let person: PersonInstance = new Person("Aisha", 25);

Contoh Kasus Dunia Nyata

Membuat Formulir Dinamis

Dalam pengembangan aplikasi web, kita sering perlu membuat formulir yang dinamis. Utility Types dapat membantu dalam kasus ini.

interface FormFields {
  username: string;
  password: string;
  rememberMe?: boolean;
}

type FormFieldsOptional = Partial<FormFields>;

function createForm(fields: FormFieldsOptional) {
  // Logika untuk membuat formulir
}

createForm({ username: "user123" });

Penjelasan:

Mengelola Response API

Ketika bekerja dengan API, kita mungkin mendapatkan data yang sebagian besar tidak kita butuhkan.

interface ApiResponse {
  id: number;
  name: string;
  email: string;
  password: string; // Tidak perlu di frontend
}

type SafeApiResponse = Omit<ApiResponse, "password">;

function handleResponse(response: SafeApiResponse) {
  // Logika untuk menangani response tanpa password
}

Pendapat Pribadi: Utility Types membantu menjaga keamanan dengan menghilangkan data sensitif seperti password dari tipe yang digunakan di frontend.

Praktik Terbaik dalam Menggunakan Utility Types

  1. Pahami Kebutuhan Anda

    Pilih Utility Type yang sesuai dengan kebutuhan spesifik Anda.

  2. Gunakan untuk Mengurangi Duplikasi Kode

    Manfaatkan Utility Types untuk menghindari penulisan ulang tipe yang mirip.

  3. Perhatikan Keamanan Tipe

    Pastikan penggunaan Utility Types tidak mengorbankan keamanan tipe dalam aplikasi.

  4. Dokumentasikan Kode Anda

    Jelaskan penggunaan Utility Types dalam kode untuk membantu tim memahami tujuan Anda.

  5. Kombinasikan dengan Type Assertion Jika Perlu

    Dalam beberapa kasus, Anda mungkin perlu menggunakan Type Assertion bersama dengan Utility Types.

Catatan: Utility Types adalah alat yang kuat, tetapi seperti semua alat, harus digunakan dengan bijak. Jangan gunakan secara berlebihan yang dapat membuat kode menjadi sulit dipahami.

Kesimpulan

Utility Types di TypeScript menawarkan cara yang efisien dan aman untuk memanipulasi tipe data. Dengan memahami dan menggunakan Utility Types seperti Partial, Required, Readonly, Pick, Omit, Record, dan lainnya, Anda dapat menulis kode yang lebih fleksibel dan mudah dipelihara.

Rekomendasi Selanjutnya:

Baca juga Artikel tentang Generic di Typescript

Pendapat Pribadi: Dalam perjalananku belajar TypeScript kembali, Utility Types telah menjadi salah satu fitur favorit saya. Mereka membantuku menulis kode yang lebih efisien dan terstruktur, terutama dalam proyek-proyek small ke mid.

Semoga artikel ini memberikan wawasan yang berguna bagi Anda. Jika Anda memiliki pertanyaan atau ingin berdiskusi lebih lanjut, jangan ragu untuk menghubungi saya. Selamat ngoding dan teruslah berinovasi!

Semoga catatan ini bermanfaat bagi saya pribadi dan teman-teman semua. Mari kita terus belajar dan berbagi ilmu untuk kemajuan bersama.