Bahasa Indonesia

Buka kekuatan tipe utilitas TypeScript untuk menulis kode yang lebih bersih, mudah dipelihara, dan aman secara tipe. Jelajahi aplikasi praktis dengan contoh dunia nyata untuk developer di seluruh dunia.

Menguasai Tipe Utilitas TypeScript: Panduan Praktis untuk Developer Global

TypeScript menawarkan seperangkat tipe utilitas bawaan yang kuat yang dapat secara signifikan meningkatkan keamanan tipe, keterbacaan, dan kemudahan pemeliharaan kode Anda. Tipe utilitas ini pada dasarnya adalah transformasi tipe yang telah ditentukan sebelumnya yang dapat Anda terapkan pada tipe yang sudah ada, sehingga Anda tidak perlu menulis kode yang berulang dan rawan kesalahan. Panduan ini akan menjelajahi berbagai tipe utilitas dengan contoh-contoh praktis yang relevan bagi para developer di seluruh dunia.

Mengapa Menggunakan Tipe Utilitas?

Tipe utilitas menjawab skenario manipulasi tipe yang umum. Dengan memanfaatkannya, Anda dapat:

Tipe Utilitas Inti

Partial

Partial membentuk sebuah tipe di mana semua properti dari T diatur menjadi opsional. Ini sangat berguna ketika Anda ingin membuat tipe untuk pembaruan parsial atau objek konfigurasi.

Contoh:

Bayangkan Anda sedang membangun platform e-commerce dengan pelanggan dari berbagai wilayah. Anda memiliki tipe Customer:


interface Customer {
  id: string;
  firstName: string;
  lastName: string;
  email: string;
  phoneNumber: string;
  address: {
    street: string;
    city: string;
    country: string;
    postalCode: string;
  };
  preferences?: {
    language: string;
    currency: string;
  }
}

Saat memperbarui informasi pelanggan, Anda mungkin tidak ingin mewajibkan semua bidang. Partial memungkinkan Anda untuk mendefinisikan sebuah tipe di mana semua properti dari Customer bersifat opsional:


type PartialCustomer = Partial<Customer>;

function updateCustomer(id: string, updates: PartialCustomer): void {
  // ... implementasi untuk memperbarui pelanggan dengan ID yang diberikan
}

updateCustomer("123", { firstName: "John", lastName: "Doe" }); // Valid
updateCustomer("456", { address: { city: "London" } }); // Valid

Readonly

Readonly membentuk sebuah tipe di mana semua properti dari T diatur menjadi readonly, mencegah modifikasi setelah inisialisasi. Ini berharga untuk memastikan imutabilitas.

Contoh:

Pertimbangkan objek konfigurasi untuk aplikasi global Anda:


interface AppConfig {
  apiUrl: string;
  theme: string;
  supportedLanguages: string[];
  version: string; // Versi ditambahkan
}

const config: AppConfig = {
  apiUrl: "https://api.example.com",
  theme: "dark",
  supportedLanguages: ["en", "fr", "de", "es", "zh"],
  version: "1.0.0"
};

Untuk mencegah modifikasi yang tidak disengaja pada konfigurasi setelah inisialisasi, Anda dapat menggunakan Readonly:


type ReadonlyAppConfig = Readonly<AppConfig>;

const readonlyConfig: ReadonlyAppConfig = {
  apiUrl: "https://api.example.com",
  theme: "dark",
  supportedLanguages: ["en", "fr", "de", "es", "zh"],
  version: "1.0.0"
};

// readonlyConfig.apiUrl = "https://newapi.example.com"; // Error: Tidak dapat menetapkan nilai ke 'apiUrl' karena ini adalah properti read-only.

Pick

Pick membentuk sebuah tipe dengan memilih sekumpulan properti K dari T, di mana K adalah gabungan dari tipe literal string yang mewakili nama properti yang ingin Anda sertakan.

Contoh:

Katakanlah Anda memiliki antarmuka (interface) Event dengan berbagai properti:


interface Event {
  id: string;
  title: string;
  description: string;
  location: string;
  startTime: Date;
  endTime: Date;
  organizer: string;
  attendees: string[];
}

Jika Anda hanya memerlukan title, location, dan startTime untuk komponen tampilan tertentu, Anda dapat menggunakan Pick:


type EventSummary = Pick<Event, "title" | "location" | "startTime">;

function displayEventSummary(event: EventSummary): void {
  console.log(`Event: ${event.title} at ${event.location} on ${event.startTime}`);
}

Omit

Omit membentuk sebuah tipe dengan mengecualikan sekumpulan properti K dari T, di mana K adalah gabungan dari tipe literal string yang mewakili nama properti yang ingin Anda kecualikan. Ini adalah kebalikan dari Pick.

Contoh:

Menggunakan antarmuka Event yang sama, jika Anda ingin membuat tipe untuk membuat acara baru, Anda mungkin ingin mengecualikan properti id, yang biasanya dibuat oleh backend:


type NewEvent = Omit<Event, "id">;

function createEvent(event: NewEvent): void {
  // ... implementasi untuk membuat acara baru
}

Record

Record membentuk tipe objek yang kunci propertinya adalah K dan nilai propertinya adalah T. K bisa berupa gabungan dari tipe literal string, tipe literal angka, atau simbol. Ini sempurna untuk membuat kamus (dictionaries) atau peta (maps).

Contoh:

Bayangkan Anda perlu menyimpan terjemahan untuk antarmuka pengguna aplikasi Anda. Anda dapat menggunakan Record untuk mendefinisikan tipe untuk terjemahan Anda:


type Translations = Record<string, string>;

const enTranslations: Translations = {
  "hello": "Hello",
  "goodbye": "Goodbye",
  "welcome": "Welcome to our platform!"
};

const frTranslations: Translations = {
  "hello": "Bonjour",
  "goodbye": "Au revoir",
  "welcome": "Bienvenue sur notre plateforme !"
};

function translate(key: string, language: string): string {
  const translations = language === "en" ? enTranslations : frTranslations; //Disederhanakan
  return translations[key] || key; // Kembali ke kunci jika tidak ada terjemahan yang ditemukan
}

console.log(translate("hello", "en")); // Output: Hello
console.log(translate("hello", "fr")); // Output: Bonjour
console.log(translate("nonexistent", "en")); // Output: nonexistent

Exclude

Exclude membentuk sebuah tipe dengan mengecualikan dari T semua anggota gabungan (union) yang dapat ditetapkan ke U. Ini berguna untuk menyaring tipe-tipe spesifik dari sebuah gabungan.

Contoh:

Anda mungkin memiliki tipe yang mewakili berbagai jenis acara:


type EventType = "concert" | "conference" | "workshop" | "webinar";

Jika Anda ingin membuat tipe yang mengecualikan acara "webinar", Anda dapat menggunakan Exclude:


type PhysicalEvent = Exclude<EventType, "webinar">;

// PhysicalEvent sekarang adalah "concert" | "conference" | "workshop"

function attendPhysicalEvent(event: PhysicalEvent): void {
  console.log(`Attending a ${event}`);
}

// attendPhysicalEvent("webinar"); // Error: Argumen tipe '"webinar"' tidak dapat ditetapkan ke parameter tipe '"concert" | "conference" | "workshop"'.

attendPhysicalEvent("concert"); // Valid

Extract

Extract membentuk sebuah tipe dengan mengekstrak dari T semua anggota gabungan (union) yang dapat ditetapkan ke U. Ini adalah kebalikan dari Exclude.

Contoh:

Menggunakan EventType yang sama, Anda dapat mengekstrak tipe acara webinar:


type OnlineEvent = Extract<EventType, "webinar">;

// OnlineEvent sekarang adalah "webinar"

function attendOnlineEvent(event: OnlineEvent): void {
  console.log(`Attending a ${event} online`);
}

attendOnlineEvent("webinar"); // Valid
// attendOnlineEvent("concert"); // Error: Argumen tipe '"concert"' tidak dapat ditetapkan ke parameter tipe '"webinar"'.

NonNullable

NonNullable membentuk sebuah tipe dengan mengecualikan null dan undefined dari T.

Contoh:


type MaybeString = string | null | undefined;

type DefinitelyString = NonNullable<MaybeString>;

// DefinitelyString sekarang adalah string

function processString(str: DefinitelyString): void {
  console.log(str.toUpperCase());
}

// processString(null); // Error: Argumen tipe 'null' tidak dapat ditetapkan ke parameter tipe 'string'.
// processString(undefined); // Error: Argumen tipe 'undefined' tidak dapat ditetapkan ke parameter tipe 'string'.
processString("hello"); // Valid

ReturnType

ReturnType membentuk sebuah tipe yang terdiri dari tipe kembalian (return type) dari fungsi T.

Contoh:


function greet(name: string): string {
  return `Hello, ${name}!`;
}

type Greeting = ReturnType<typeof greet>;

// Greeting sekarang adalah string

const message: Greeting = greet("World");

console.log(message);

Parameters

Parameters membentuk sebuah tipe tuple dari tipe-tipe parameter sebuah tipe fungsi T.

Contoh:


function logEvent(eventName: string, eventData: object): void {
  console.log(`Event: ${eventName}`, eventData);
}

type LogEventParams = Parameters<typeof logEvent>;

// LogEventParams sekarang adalah [eventName: string, eventData: object]

const params: LogEventParams = ["user_login", { userId: "123", timestamp: Date.now() }];

logEvent(...params);

ConstructorParameters

ConstructorParameters membentuk tipe tuple atau array dari tipe-tipe parameter sebuah tipe fungsi konstruktor T. Ini menyimpulkan tipe-tipe argumen yang perlu dilewatkan ke konstruktor sebuah kelas.

Contoh:


class Greeter {
  greeting: string;

  constructor(message: string) {
    this.greeting = message;
  }

  greet() {
    return "Hello, " + this.greeting;
  }
}


type GreeterParams = ConstructorParameters<typeof Greeter>;

// GreeterParams sekarang adalah [message: string]

const paramsGreeter: GreeterParams = ["World"];
const greeterInstance = new Greeter(...paramsGreeter);

console.log(greeterInstance.greet()); // Menghasilkan: Hello, World

Required

Required membentuk sebuah tipe yang terdiri dari semua properti dari T yang diatur menjadi wajib (required). Ini membuat semua properti opsional menjadi wajib.

Contoh:


interface UserProfile {
  name: string;
  age?: number;
  email?: string;
}

type RequiredUserProfile = Required<UserProfile>;

// RequiredUserProfile sekarang adalah { name: string; age: number; email: string; }

const completeProfile: RequiredUserProfile = {
  name: "Alice",
  age: 30,
  email: "alice@example.com"
};

// const incompleteProfile: RequiredUserProfile = { name: "Bob" }; // Error: Properti 'age' hilang dalam tipe '{ name: string; }' tetapi wajib dalam tipe 'Required'.

Tipe Utilitas Tingkat Lanjut

Tipe Literal Template

Tipe literal template memungkinkan Anda untuk membentuk tipe literal string baru dengan menggabungkan tipe literal string yang ada, tipe literal angka, dan banyak lagi. Ini memungkinkan manipulasi tipe berbasis string yang kuat.

Contoh:


type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type APIEndpoint = `/api/users` | `/api/products`;

type RequestURL = `${HTTPMethod} ${APIEndpoint}`;

// RequestURL sekarang adalah "GET /api/users" | "POST /api/users" | "PUT /api/users" | "DELETE /api/users" | "GET /api/products" | "POST /api/products" | "PUT /api/products" | "DELETE /api/products"

function makeRequest(url: RequestURL): void {
  console.log(`Making request to ${url}`);
}

makeRequest("GET /api/users"); // Valid
// makeRequest("INVALID /api/users"); // Error

Tipe Kondisional

Tipe kondisional memungkinkan Anda untuk mendefinisikan tipe yang bergantung pada suatu kondisi yang diekspresikan sebagai hubungan tipe. Mereka menggunakan kata kunci infer untuk mengekstrak informasi tipe.

Contoh:


type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

// Jika T adalah Promise, maka tipenya adalah U; jika tidak, tipenya adalah T.

async function fetchData(): Promise<number> {
  return 42;
}


type Data = UnwrapPromise<ReturnType<typeof fetchData>>;

// Data sekarang adalah number

function processData(data: Data): void {
  console.log(data * 2);
}

processData(await fetchData());

Aplikasi Praktis dan Skenario Dunia Nyata

Mari kita jelajahi skenario dunia nyata yang lebih kompleks di mana tipe utilitas bersinar.

1. Penanganan Formulir

Saat berurusan dengan formulir, Anda sering memiliki skenario di mana Anda perlu merepresentasikan nilai formulir awal, nilai formulir yang diperbarui, dan nilai akhir yang dikirim. Tipe utilitas dapat membantu Anda mengelola berbagai status ini secara efisien.


interface FormData {
  firstName: string;
  lastName: string;
  email: string;
  country: string; // Wajib
  city?: string; // Opsional
  postalCode?: string;
  newsletterSubscription?: boolean;
}

// Nilai formulir awal (bidang opsional)
type InitialFormValues = Partial<FormData>;

// Nilai formulir yang diperbarui (beberapa bidang mungkin hilang)
type UpdatedFormValues = Partial<FormData>;

// Bidang yang wajib untuk pengiriman
type RequiredForSubmission = Required<Pick<FormData, 'firstName' | 'lastName' | 'email' | 'country'>>;

// Gunakan tipe-tipe ini di komponen formulir Anda
function initializeForm(initialValues: InitialFormValues): void { }
function updateForm(updates: UpdatedFormValues): void {}
function submitForm(data: RequiredForSubmission): void {}


const initialForm: InitialFormValues = { newsletterSubscription: true };

const updateFormValues: UpdatedFormValues = {
    firstName: "John",
    lastName: "Doe"
};

// const submissionData: RequiredForSubmission = { firstName: "test", lastName: "test", email: "test" }; // ERROR: Properti 'country' tidak ada
const submissionData: RequiredForSubmission = { firstName: "test", lastName: "test", email: "test", country: "USA" }; //OK


2. Transformasi Data API

Saat menggunakan data dari API, Anda mungkin perlu mengubah data ke format yang berbeda untuk aplikasi Anda. Tipe utilitas dapat membantu Anda mendefinisikan struktur data yang ditransformasi.


interface APIResponse {
  user_id: string;
  first_name: string;
  last_name: string;
  email_address: string;
  profile_picture_url: string;
  is_active: boolean;
}

// Ubah respons API ke format yang lebih mudah dibaca
type UserData = {
  id: string;
  fullName: string;
  email: string;
  avatar: string;
  active: boolean;
};

function transformApiResponse(response: APIResponse): UserData {
  return {
    id: response.user_id,
    fullName: `${response.first_name} ${response.last_name}`,
    email: response.email_address,
    avatar: response.profile_picture_url,
    active: response.is_active
  };
}

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


// Anda bahkan dapat memaksakan tipe dengan:

function saferTransformApiResponse(response: APIResponse): UserData {
    const {user_id, first_name, last_name, email_address, profile_picture_url, is_active} = response;
    const transformed: UserData = {
        id: user_id,
        fullName: `${first_name} ${last_name}`,
        email: email_address,
        avatar: profile_picture_url,
        active: is_active
    };

    return transformed;
}

3. Menangani Objek Konfigurasi

Objek konfigurasi umum di banyak aplikasi. Tipe utilitas dapat membantu Anda mendefinisikan struktur objek konfigurasi dan memastikan bahwa itu digunakan dengan benar.


interface AppSettings {
  theme: "light" | "dark";
  language: string;
  notificationsEnabled: boolean;
  apiUrl?: string; // URL API opsional untuk lingkungan yang berbeda
  timeout?: number;  //Opsional
}

// Pengaturan default
const defaultSettings: AppSettings = {
  theme: "light",
  language: "en",
  notificationsEnabled: true
};

// Fungsi untuk menggabungkan pengaturan pengguna dengan pengaturan default
function mergeSettings(userSettings: Partial<AppSettings>): AppSettings {
  return { ...defaultSettings, ...userSettings };
}

// Gunakan pengaturan yang digabungkan di aplikasi Anda
const mergedSettings = mergeSettings({ theme: "dark", apiUrl: "https://customapi.example.com" });
console.log(mergedSettings);

Tips untuk Penggunaan Tipe Utilitas yang Efektif

  • Mulai dari yang sederhana: Mulailah dengan tipe utilitas dasar seperti Partial dan Readonly sebelum beralih ke yang lebih kompleks.
  • Gunakan nama deskriptif: Berikan alias tipe Anda nama yang bermakna untuk meningkatkan keterbacaan.
  • Gabungkan tipe utilitas: Anda dapat menggabungkan beberapa tipe utilitas untuk mencapai transformasi tipe yang kompleks.
  • Manfaatkan dukungan editor: Manfaatkan dukungan editor TypeScript yang sangat baik untuk menjelajahi efek dari tipe utilitas.
  • Pahami konsep dasarnya: Pemahaman yang kuat tentang sistem tipe TypeScript sangat penting untuk penggunaan tipe utilitas yang efektif.

Kesimpulan

Tipe utilitas TypeScript adalah alat yang kuat yang dapat secara signifikan meningkatkan kualitas dan kemudahan pemeliharaan kode Anda. Dengan memahami dan menerapkan tipe utilitas ini secara efektif, Anda dapat menulis aplikasi yang lebih bersih, lebih aman secara tipe, dan lebih tangguh yang memenuhi tuntutan lanskap pengembangan global. Panduan ini telah memberikan gambaran komprehensif tentang tipe utilitas umum dan contoh praktis. Bereksperimenlah dengannya dan jelajahi potensinya untuk menyempurnakan proyek TypeScript Anda. Ingatlah untuk memprioritaskan keterbacaan dan kejelasan saat menggunakan tipe utilitas, dan selalu berusaha untuk menulis kode yang mudah dipahami dan dipelihara, di mana pun lokasi rekan developer Anda berada.