Bahasa Indonesia

Kuasai tipe utilitas TypeScript: alat canggih untuk transformasi tipe, meningkatkan penggunaan kembali kode, dan memperkuat keamanan tipe di aplikasi Anda.

Tipe Utilitas TypeScript: Alat Bawaan untuk Manipulasi Tipe

TypeScript adalah bahasa yang kuat yang membawa pengetikan statis ke JavaScript. Salah satu fitur utamanya adalah kemampuan untuk memanipulasi tipe, memungkinkan pengembang untuk membuat kode yang lebih kuat dan mudah dipelihara. TypeScript menyediakan serangkaian tipe utilitas bawaan yang menyederhanakan transformasi tipe umum. Tipe utilitas ini adalah alat yang sangat berharga untuk meningkatkan keamanan tipe, meningkatkan penggunaan kembali kode, dan merampingkan alur kerja pengembangan Anda. Panduan komprehensif ini menjelajahi tipe utilitas TypeScript yang paling penting, memberikan contoh praktis dan wawasan yang dapat ditindaklanjuti untuk membantu Anda menguasainya.

Apa itu Tipe Utilitas TypeScript?

Tipe utilitas adalah operator tipe yang telah ditentukan sebelumnya yang mengubah tipe yang ada menjadi tipe baru. Tipe ini sudah terpasang di dalam bahasa TypeScript dan menyediakan cara yang ringkas dan deklaratif untuk melakukan manipulasi tipe umum. Menggunakan tipe utilitas dapat secara signifikan mengurangi kode boilerplate dan membuat definisi tipe Anda lebih ekspresif dan lebih mudah dipahami.

Anggap saja mereka sebagai fungsi yang beroperasi pada tipe, bukan pada nilai. Mereka mengambil tipe sebagai input dan mengembalikan tipe yang dimodifikasi sebagai output. Ini memungkinkan Anda untuk membuat hubungan dan transformasi tipe yang kompleks dengan kode minimal.

Mengapa Menggunakan Tipe Utilitas?

Ada beberapa alasan kuat untuk memasukkan tipe utilitas ke dalam proyek TypeScript Anda:

Tipe Utilitas TypeScript Esensial

Mari kita jelajahi beberapa tipe utilitas yang paling umum digunakan dan bermanfaat di TypeScript. Kita akan membahas tujuan, sintaks, dan memberikan contoh praktis untuk mengilustrasikan penggunaannya.

1. Partial<T>

Tipe utilitas Partial<T> membuat semua properti dari tipe T menjadi opsional. Ini berguna ketika Anda ingin membuat tipe baru yang memiliki beberapa atau semua properti dari tipe yang sudah ada, tetapi Anda tidak ingin mengharuskan semuanya ada.

Sintaks:

type Partial<T> = { [P in keyof T]?: T[P]; };

Contoh:

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

type OptionalUser = Partial<User>; // Semua properti sekarang opsional

const partialUser: OptionalUser = {
 name: "Alice", // Hanya menyediakan properti nama
};

Kasus Penggunaan: Memperbarui objek dengan hanya properti tertentu. Sebagai contoh, bayangkan formulir pembaruan profil pengguna. Anda tidak ingin mengharuskan pengguna untuk memperbarui setiap bidang sekaligus.

2. Required<T>

Tipe utilitas Required<T> membuat semua properti dari tipe T menjadi wajib. Ini adalah kebalikan dari Partial<T>. Ini berguna ketika Anda memiliki tipe dengan properti opsional, dan Anda ingin memastikan bahwa semua properti ada.

Sintaks:

type Required<T> = { [P in keyof T]-?: T[P]; };

Contoh:

interface Config {
 apiKey?: string;
 apiUrl?: string;
}

type CompleteConfig = Required<Config>; // Semua properti sekarang wajib

const config: CompleteConfig = {
 apiKey: "your-api-key",
 apiUrl: "https://example.com/api",
};

Kasus Penggunaan: Memastikan bahwa semua pengaturan konfigurasi disediakan sebelum memulai aplikasi. Ini dapat membantu mencegah kesalahan runtime yang disebabkan oleh pengaturan yang hilang atau tidak terdefinisi.

3. Readonly<T>

Tipe utilitas Readonly<T> membuat semua properti dari tipe T menjadi `readonly` (hanya bisa dibaca). Ini mencegah Anda secara tidak sengaja mengubah properti objek setelah dibuat. Ini mempromosikan imutabilitas dan meningkatkan prediktabilitas kode Anda.

Sintaks:

type Readonly<T> = { readonly [P in keyof T]: T[P]; };

Contoh:

interface Product {
 id: number;
 name: string;
 price: number;
}

type ImmutableProduct = Readonly<Product>; // Semua properti sekarang readonly

const product: ImmutableProduct = {
 id: 123,
 name: "Example Product",
 price: 25.99,
};

// product.price = 29.99; // Error: Tidak dapat menetapkan ke 'price' karena ini adalah properti read-only.

Kasus Penggunaan: Membuat struktur data yang tidak dapat diubah (immutable), seperti objek konfigurasi atau objek transfer data (DTO), yang tidak boleh dimodifikasi setelah dibuat. Ini sangat berguna dalam paradigma pemrograman fungsional.

4. Pick<T, K extends keyof T>

Tipe utilitas Pick<T, K extends keyof T> membuat tipe baru dengan memilih satu set properti K dari tipe T. Ini berguna ketika Anda hanya memerlukan sebagian dari properti dari tipe yang ada.

Sintaks:

type Pick<T, K extends keyof T> = { [P in K]: T[P]; };

Contoh:

interface Employee {
 id: number;
 name: string;
 department: string;
salary: number;
}

type EmployeeNameAndDepartment = Pick<Employee, "name" | "department">; // Hanya pilih nama dan departemen

const employeeInfo: EmployeeNameAndDepartment = {
 name: "Bob",
 department: "Engineering",
};

Kasus Penggunaan: Membuat objek transfer data (DTO) khusus yang hanya berisi data yang diperlukan untuk operasi tertentu. Ini dapat meningkatkan kinerja dan mengurangi jumlah data yang ditransmisikan melalui jaringan. Bayangkan mengirim detail pengguna ke klien tetapi mengecualikan informasi sensitif seperti gaji. Anda bisa menggunakan Pick untuk hanya mengirim `id` dan `name`.

5. Omit<T, K extends keyof any>

Tipe utilitas Omit<T, K extends keyof any> membuat tipe baru dengan menghilangkan satu set properti K dari tipe T. Ini adalah kebalikan dari Pick<T, K extends keyof T> dan berguna ketika Anda ingin mengecualikan properti tertentu dari tipe yang sudah ada.

Sintaks:

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

Contoh:

interface Event {
 id: number;
 title: string;
description: string;
 date: Date;
 location: string;
}

type EventSummary = Omit<Event, "description" | "location">; // Hilangkan deskripsi dan lokasi

const eventPreview: EventSummary = {
 id: 1,
 title: "Conference",
 date: new Date(),
};

Kasus Penggunaan: Membuat versi model data yang disederhanakan untuk tujuan tertentu, seperti menampilkan ringkasan acara tanpa menyertakan deskripsi dan lokasi lengkap. Ini juga dapat digunakan untuk menghapus bidang sensitif sebelum mengirim data ke klien.

6. Exclude<T, U>

Tipe utilitas Exclude<T, U> membuat tipe baru dengan mengecualikan dari T semua tipe yang dapat ditetapkan ke U. Ini berguna ketika Anda ingin menghapus tipe tertentu dari tipe union.

Sintaks:

type Exclude<T, U> = T extends U ? never : T;

Contoh:

type AllowedFileTypes = "image" | "video" | "audio" | "document";
type MediaFileTypes = "image" | "video" | "audio";

type DocumentFileTypes = Exclude<AllowedFileTypes, MediaFileTypes>; // "document"

const fileType: DocumentFileTypes = "document";

Kasus Penggunaan: Menyaring tipe union untuk menghapus tipe spesifik yang tidak relevan dalam konteks tertentu. Misalnya, Anda mungkin ingin mengecualikan jenis file tertentu dari daftar jenis file yang diizinkan.

7. Extract<T, U>

Tipe utilitas Extract<T, U> membuat tipe baru dengan mengekstraksi dari T semua tipe yang dapat ditetapkan ke U. Ini adalah kebalikan dari Exclude<T, U> dan berguna ketika Anda ingin memilih tipe tertentu dari tipe union.

Sintaks:

type Extract<T, U> = T extends U ? T : never;

Contoh:

type InputTypes = string | number | boolean | null | undefined;
type PrimitiveTypes = string | number | boolean;

type NonNullablePrimitives = Extract<InputTypes, PrimitiveTypes>; // string | number | boolean

const value: NonNullablePrimitives = "hello";

Kasus Penggunaan: Memilih tipe spesifik dari tipe union berdasarkan kriteria tertentu. Misalnya, Anda mungkin ingin mengekstrak semua tipe primitif dari tipe union yang mencakup tipe primitif dan tipe objek.

8. NonNullable<T>

Tipe utilitas NonNullable<T> membuat tipe baru dengan mengecualikan null dan undefined dari tipe T. Ini berguna ketika Anda ingin memastikan bahwa suatu tipe tidak boleh null atau undefined.

Sintaks:

type NonNullable<T> = T extends null | undefined ? never : T;

Contoh:

type MaybeString = string | null | undefined;

type DefinitelyString = NonNullable<MaybeString>; // string

const message: DefinitelyString = "Hello, world!";

Kasus Penggunaan: Memastikan bahwa suatu nilai bukan null atau undefined sebelum melakukan operasi padanya. Ini dapat membantu mencegah kesalahan runtime yang disebabkan oleh nilai null atau undefined yang tidak terduga. Pertimbangkan skenario di mana Anda perlu memproses alamat pengguna, dan sangat penting bahwa alamat tersebut tidak null sebelum operasi apa pun.

9. ReturnType<T extends (...args: any) => any>

Tipe utilitas ReturnType<T extends (...args: any) => any> mengekstraksi tipe kembalian (return type) dari tipe fungsi T. Ini berguna ketika Anda ingin mengetahui tipe nilai yang dikembalikan oleh suatu fungsi.

Sintaks:

type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

Contoh:

function fetchData(url: string): Promise<{ data: any }> {
 return fetch(url).then(response => response.json());
}

type FetchDataReturnType = ReturnType<typeof fetchData>; // Promise<{ data: any }>

async function processData(data: FetchDataReturnType) {
 // ...
}

Kasus Penggunaan: Menentukan tipe nilai yang dikembalikan oleh suatu fungsi, terutama ketika berurusan dengan operasi asinkron atau tanda tangan fungsi yang kompleks. Ini memungkinkan Anda untuk memastikan bahwa Anda menangani nilai yang dikembalikan dengan benar.

10. Parameters<T extends (...args: any) => any>

Tipe utilitas Parameters<T extends (...args: any) => any> mengekstraksi tipe parameter dari tipe fungsi T sebagai sebuah tuple. Ini berguna ketika Anda ingin mengetahui tipe argumen yang diterima oleh suatu fungsi.

Sintaks:

type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

Contoh:

function createUser(name: string, age: number, email: string): void {
 // ...
}

type CreateUserParams = Parameters<typeof createUser>; // [string, number, string]

function logUser(...args: CreateUserParams) {
 console.log("Creating user with:", args);
}

Kasus Penggunaan: Menentukan tipe argumen yang diterima oleh suatu fungsi, yang dapat berguna untuk membuat fungsi generik atau dekorator yang perlu bekerja dengan fungsi dengan tanda tangan yang berbeda. Ini membantu memastikan keamanan tipe saat meneruskan argumen ke fungsi secara dinamis.

11. ConstructorParameters<T extends abstract new (...args: any) => any>

Tipe utilitas ConstructorParameters<T extends abstract new (...args: any) => any> mengekstraksi tipe parameter dari tipe fungsi konstruktor T sebagai sebuah tuple. Ini berguna ketika Anda ingin mengetahui tipe argumen yang diterima oleh sebuah konstruktor.

Sintaks:

type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never;

Contoh:

class Logger {
 constructor(public prefix: string, public enabled: boolean) {}
 log(message: string) {
 if (this.enabled) {
 console.log(`${this.prefix}: ${message}`);
 }
 }
}

type LoggerConstructorParams = ConstructorParameters<typeof Logger>; // [string, boolean]

function createLogger(...args: LoggerConstructorParams) {
 return new Logger(...args);
}

Kasus Penggunaan: Mirip dengan Parameters, tetapi khusus untuk fungsi konstruktor. Ini membantu saat membuat pabrik (factories) atau sistem injeksi dependensi di mana Anda perlu membuat instance kelas secara dinamis dengan tanda tangan konstruktor yang berbeda.

12. InstanceType<T extends abstract new (...args: any) => any>

Tipe utilitas InstanceType<T extends abstract new (...args: any) => any> mengekstraksi tipe instans dari tipe fungsi konstruktor T. Ini berguna ketika Anda ingin mengetahui tipe objek yang dibuat oleh sebuah konstruktor.

Sintaks:

type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;

Contoh:

class Greeter {
 greeting: string;
 constructor(message: string) {
 this.greeting = message;
 }
 greet() {
 return "Hello, " + this.greeting;
 }
}

type GreeterInstance = InstanceType<typeof Greeter>; // Greeter

const myGreeter: GreeterInstance = new Greeter("World");
console.log(myGreeter.greet());

Kasus Penggunaan: Menentukan tipe objek yang dibuat oleh konstruktor, yang berguna saat bekerja dengan pewarisan atau polimorfisme. Ini menyediakan cara yang aman dari segi tipe untuk merujuk ke instance sebuah kelas.

13. Record<K extends keyof any, T>

Tipe utilitas Record<K extends keyof any, T> membangun sebuah tipe objek yang kunci propertinya adalah K dan nilai propertinya adalah T. Ini berguna untuk membuat tipe seperti kamus (dictionary) di mana Anda sudah mengetahui kuncinya terlebih dahulu.

Sintaks:

type Record<K extends keyof any, T> = { [P in K]: T; };

Contoh:

type CountryCode = "US" | "CA" | "GB" | "DE";

type CurrencyMap = Record<CountryCode, string>; // { US: string; CA: string; GB: string; DE: string; }

const currencies: CurrencyMap = {
 US: "USD",
 CA: "CAD",
 GB: "GBP",
 DE: "EUR",
};

Kasus Penggunaan: Membuat objek seperti kamus di mana Anda memiliki satu set kunci yang tetap dan ingin memastikan bahwa semua kunci memiliki nilai dari tipe tertentu. Ini umum terjadi saat bekerja dengan file konfigurasi, pemetaan data, atau tabel pencarian (lookup tables).

Tipe Utilitas Kustom

Meskipun tipe utilitas bawaan TypeScript sangat kuat, Anda juga dapat membuat tipe utilitas kustom Anda sendiri untuk mengatasi kebutuhan spesifik dalam proyek Anda. Ini memungkinkan Anda untuk mengenkapsulasi transformasi tipe yang kompleks dan menggunakannya kembali di seluruh basis kode Anda.

Contoh:

// Tipe utilitas untuk mendapatkan kunci dari objek yang memiliki tipe tertentu
type KeysOfType<T, U> = { [K in keyof T]: T[K] extends U ? K : never }[keyof T];

interface Person {
 name: string;
 age: number;
 address: string;
 phoneNumber: number;
}

type StringKeys = KeysOfType<Person, string>; // "name" | "address"

Praktik Terbaik Menggunakan Tipe Utilitas

Kesimpulan

Tipe utilitas TypeScript adalah alat canggih yang dapat secara signifikan meningkatkan keamanan tipe, penggunaan kembali, dan kemudahan pemeliharaan kode Anda. Dengan menguasai tipe utilitas ini, Anda dapat menulis aplikasi TypeScript yang lebih kuat dan ekspresif. Panduan ini telah membahas tipe utilitas TypeScript yang paling penting, memberikan contoh praktis dan wawasan yang dapat ditindaklanjuti untuk membantu Anda memasukkannya ke dalam proyek Anda.

Ingatlah untuk bereksperimen dengan tipe utilitas ini dan jelajahi bagaimana mereka dapat digunakan untuk memecahkan masalah spesifik dalam kode Anda sendiri. Seiring Anda menjadi lebih akrab dengan mereka, Anda akan mendapati diri Anda semakin sering menggunakannya untuk membuat aplikasi TypeScript yang lebih bersih, lebih mudah dipelihara, dan lebih aman dari segi tipe. Baik Anda membangun aplikasi web, aplikasi sisi server, atau apa pun di antaranya, tipe utilitas menyediakan serangkaian alat yang berharga untuk meningkatkan alur kerja pengembangan Anda dan kualitas kode Anda. Dengan memanfaatkan alat manipulasi tipe bawaan ini, Anda dapat membuka potensi penuh TypeScript dan menulis kode yang ekspresif dan kuat.