Bahasa Indonesia

Jelajahi tipe literal TypeScript, fitur canggih untuk batasan nilai yang ketat, meningkatkan kejelasan kode, dan mencegah eror. Pelajari dengan contoh praktis.

Tipe Literal TypeScript: Menguasai Batasan Nilai yang Tepat

TypeScript, sebuah superset dari JavaScript, membawa pengetikan statis ke dunia pengembangan web yang dinamis. Salah satu fiturnya yang paling kuat adalah konsep tipe literal. Tipe literal memungkinkan Anda untuk menentukan nilai pasti yang dapat dipegang oleh variabel atau properti, memberikan keamanan tipe yang ditingkatkan dan mencegah kesalahan tak terduga. Artikel ini akan menjelajahi tipe literal secara mendalam, mencakup sintaksis, penggunaan, dan manfaatnya dengan contoh-contoh praktis.

Apa itu Tipe Literal?

Berbeda dengan tipe tradisional seperti string, number, atau boolean, tipe literal tidak mewakili kategori nilai yang luas. Sebaliknya, mereka mewakili nilai-nilai spesifik yang tetap. TypeScript mendukung tiga jenis tipe literal:

Dengan menggunakan tipe literal, Anda dapat membuat definisi tipe yang lebih presisi yang mencerminkan batasan data Anda yang sebenarnya, menghasilkan kode yang lebih tangguh dan mudah dipelihara.

Tipe Literal String

Tipe literal string adalah jenis literal yang paling umum digunakan. Tipe ini memungkinkan Anda untuk menentukan bahwa sebuah variabel atau properti hanya dapat menampung salah satu dari sekumpulan nilai string yang telah ditentukan sebelumnya.

Sintaksis Dasar

Sintaksis untuk mendefinisikan tipe literal string cukup sederhana:


type AllowedValues = "value1" | "value2" | "value3";

Ini mendefinisikan sebuah tipe bernama AllowedValues yang hanya dapat menampung string "value1", "value2", atau "value3".

Contoh Praktis

1. Mendefinisikan Palet Warna:

Bayangkan Anda sedang membangun sebuah pustaka antarmuka pengguna (UI library) dan ingin memastikan bahwa pengguna hanya dapat menentukan warna dari palet yang telah ditentukan sebelumnya:


type Color = "red" | "green" | "blue" | "yellow";

function paintElement(element: HTMLElement, color: Color) {
  element.style.backgroundColor = color;
}

paintElement(document.getElementById("myElement")!, "red"); // Valid
paintElement(document.getElementById("myElement")!, "purple"); // Error: Argument of type '"purple"' is not assignable to parameter of type 'Color'.

Contoh ini menunjukkan bagaimana tipe literal string dapat menerapkan sekumpulan nilai yang diizinkan secara ketat, mencegah pengembang secara tidak sengaja menggunakan warna yang tidak valid.

2. Mendefinisikan Endpoint API:

Saat bekerja dengan API, Anda sering kali perlu menentukan endpoint yang diizinkan. Tipe literal string dapat membantu menerapkan hal ini:


type APIEndpoint = "/users" | "/posts" | "/comments";

function fetchData(endpoint: APIEndpoint) {
  // ... implementasi untuk mengambil data dari endpoint yang ditentukan
  console.log(`Fetching data from ${endpoint}`);
}

fetchData("/users"); // Valid
fetchData("/products"); // Error: Argument of type '"/products"' is not assignable to parameter of type 'APIEndpoint'.

Contoh ini memastikan bahwa fungsi fetchData hanya dapat dipanggil dengan endpoint API yang valid, mengurangi risiko kesalahan yang disebabkan oleh salah ketik atau nama endpoint yang salah.

3. Menangani Bahasa yang Berbeda (Internasionalisasi - i18n):

Dalam aplikasi global, Anda mungkin perlu menangani berbagai bahasa. Anda dapat menggunakan tipe literal string untuk memastikan aplikasi Anda hanya mendukung bahasa yang telah ditentukan:


type Language = "en" | "es" | "fr" | "de" | "zh";

function translate(text: string, language: Language): string {
  // ... implementasi untuk menerjemahkan teks ke bahasa yang ditentukan
  console.log(`Translating '${text}' to ${language}`);
  return "Translated text"; // Placeholder
}

translate("Hello", "en"); // Valid
translate("Hello", "ja"); // Error: Argument of type '"ja"' is not assignable to parameter of type 'Language'.

Contoh ini menunjukkan cara memastikan bahwa hanya bahasa yang didukung yang digunakan dalam aplikasi Anda.

Tipe Literal Angka

Tipe literal angka memungkinkan Anda untuk menentukan bahwa sebuah variabel atau properti hanya dapat menampung nilai numerik tertentu.

Sintaksis Dasar

Sintaksis untuk mendefinisikan tipe literal angka mirip dengan tipe literal string:


type StatusCode = 200 | 404 | 500;

Ini mendefinisikan sebuah tipe bernama StatusCode yang hanya dapat menampung angka 200, 404, atau 500.

Contoh Praktis

1. Mendefinisikan Kode Status HTTP:

Anda dapat menggunakan tipe literal angka untuk merepresentasikan kode status HTTP, memastikan bahwa hanya kode yang valid yang digunakan dalam aplikasi Anda:


type HTTPStatus = 200 | 400 | 401 | 403 | 404 | 500;

function handleResponse(status: HTTPStatus) {
  switch (status) {
    case 200:
      console.log("Success!");
      break;
    case 400:
      console.log("Bad Request");
      break;
    // ... kasus lain
    default:
      console.log("Unknown Status");
  }
}

handleResponse(200); // Valid
handleResponse(600); // Error: Argument of type '600' is not assignable to parameter of type 'HTTPStatus'.

Contoh ini menerapkan penggunaan kode status HTTP yang valid, mencegah kesalahan yang disebabkan oleh penggunaan kode yang salah atau tidak standar.

2. Merepresentasikan Opsi Tetap:

Anda dapat menggunakan tipe literal angka untuk merepresentasikan opsi tetap dalam sebuah objek konfigurasi:


type RetryAttempts = 1 | 3 | 5;

interface Config {
  retryAttempts: RetryAttempts;
}

const config1: Config = { retryAttempts: 3 }; // Valid
const config2: Config = { retryAttempts: 7 }; // Error: Type '{ retryAttempts: 7; }' is not assignable to type 'Config'.

Contoh ini membatasi nilai yang mungkin untuk retryAttempts ke sebuah set spesifik, meningkatkan kejelasan dan keandalan konfigurasi Anda.

Tipe Literal Boolean

Tipe literal boolean merepresentasikan nilai spesifik true atau false. Meskipun mungkin tampak kurang serbaguna dibandingkan tipe literal string atau angka, tipe ini dapat berguna dalam skenario-skenario tertentu.

Sintaksis Dasar

Sintaksis untuk mendefinisikan tipe literal boolean adalah:


type IsEnabled = true | false;

Namun, menggunakan true | false secara langsung bersifat redundan karena setara dengan tipe boolean. Tipe literal boolean lebih berguna ketika digabungkan dengan tipe lain atau dalam tipe kondisional.

Contoh Praktis

1. Logika Kondisional dengan Konfigurasi:

Anda dapat menggunakan tipe literal boolean untuk mengontrol perilaku sebuah fungsi berdasarkan bendera konfigurasi (configuration flag):


interface FeatureFlags {
  darkMode: boolean;
  newUserFlow: boolean;
}

function initializeApp(flags: FeatureFlags) {
  if (flags.darkMode) {
    // Aktifkan mode gelap
    console.log("Enabling dark mode...");
  } else {
    // Gunakan mode terang
    console.log("Using light mode...");
  }

  if (flags.newUserFlow) {
    // Aktifkan alur pengguna baru
    console.log("Enabling new user flow...");
  } else {
    // Gunakan alur pengguna lama
    console.log("Using old user flow...");
  }
}

initializeApp({ darkMode: true, newUserFlow: false });

Meskipun contoh ini menggunakan tipe boolean standar, Anda dapat menggabungkannya dengan tipe kondisional (dijelaskan nanti) untuk menciptakan perilaku yang lebih kompleks.

2. Discriminated Unions:

Tipe literal boolean dapat digunakan sebagai diskriminator dalam tipe union. Perhatikan contoh berikut:


interface SuccessResult {
  success: true;
  data: any;
}

interface ErrorResult {
  success: false;
  error: string;
}

type Result = SuccessResult | ErrorResult;

function processResult(result: Result) {
  if (result.success) {
    console.log("Success:", result.data);
  } else {
    console.error("Error:", result.error);
  }
}

processResult({ success: true, data: { name: "John" } });
processResult({ success: false, error: "Failed to fetch data" });

Di sini, properti success, yang merupakan tipe literal boolean, bertindak sebagai diskriminator, memungkinkan TypeScript untuk mempersempit tipe dari result di dalam pernyataan if.

Menggabungkan Tipe Literal dengan Tipe Union

Tipe literal menjadi paling kuat ketika digabungkan dengan tipe union (menggunakan operator |). Ini memungkinkan Anda untuk mendefinisikan sebuah tipe yang dapat menampung salah satu dari beberapa nilai spesifik.

Contoh Praktis

1. Mendefinisikan Tipe Status:


type Status = "pending" | "in progress" | "completed" | "failed";

interface Task {
  id: number;
  description: string;
  status: Status;
}

const task1: Task = { id: 1, description: "Implement login", status: "in progress" }; // Valid
const task2: Task = { id: 2, description: "Implement logout", status: "done" };       // Error: Type '{ id: number; description: string; status: string; }' is not assignable to type 'Task'.

Contoh ini menunjukkan cara menerapkan sekumpulan nilai status yang diizinkan secara spesifik untuk objek Task.

2. Mendefinisikan Tipe Perangkat:

Dalam aplikasi seluler, Anda mungkin perlu menangani berbagai jenis perangkat. Anda dapat menggunakan gabungan (union) dari tipe literal string untuk merepresentasikannya:


type DeviceType = "mobile" | "tablet" | "desktop";

function logDeviceType(device: DeviceType) {
  console.log(`Device type: ${device}`);
}

logDeviceType("mobile"); // Valid
logDeviceType("smartwatch"); // Error: Argument of type '"smartwatch"' is not assignable to parameter of type 'DeviceType'.

Contoh ini memastikan bahwa fungsi logDeviceType hanya dipanggil dengan tipe perangkat yang valid.

Tipe Literal dengan Alias Tipe

Alias tipe (menggunakan kata kunci type) menyediakan cara untuk memberikan nama pada tipe literal, membuat kode Anda lebih mudah dibaca dan dipelihara.

Contoh Praktis

1. Mendefinisikan Tipe Kode Mata Uang:


type CurrencyCode = "USD" | "EUR" | "GBP" | "JPY";

function formatCurrency(amount: number, currency: CurrencyCode): string {
  // ... implementasi untuk memformat jumlah berdasarkan kode mata uang
  console.log(`Formatting ${amount} in ${currency}`);
  return "Formatted amount"; // Placeholder
}

formatCurrency(100, "USD"); // Valid
formatCurrency(200, "CAD"); // Error: Argument of type '"CAD"' is not assignable to parameter of type 'CurrencyCode'.

Contoh ini mendefinisikan alias tipe CurrencyCode untuk sekumpulan kode mata uang, meningkatkan keterbacaan fungsi formatCurrency.

2. Mendefinisikan Tipe Hari dalam Seminggu:


type DayOfWeek = "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday" | "Sunday";

function isWeekend(day: DayOfWeek): boolean {
  return day === "Saturday" || day === "Sunday";
}

console.log(isWeekend("Monday"));   // false
console.log(isWeekend("Saturday")); // true
console.log(isWeekend("Funday"));   // Error: Argument of type '"Funday"' is not assignable to parameter of type 'DayOfWeek'.

Inferensi Literal

TypeScript sering kali dapat menyimpulkan (infer) tipe literal secara otomatis berdasarkan nilai yang Anda tetapkan ke variabel. Ini sangat berguna saat bekerja dengan variabel const.

Contoh Praktis

1. Inferensi Tipe Literal String:


const apiKey = "your-api-key"; // TypeScript menyimpulkan tipe apiKey sebagai "your-api-key"

function validateApiKey(key: "your-api-key") {
  return key === "your-api-key";
}

console.log(validateApiKey(apiKey)); // true

const anotherKey = "invalid-key";
console.log(validateApiKey(anotherKey)); // Error: Argument of type 'string' is not assignable to parameter of type '"your-api-key"'.

Dalam contoh ini, TypeScript menyimpulkan tipe dari apiKey sebagai tipe literal string "your-api-key". Namun, jika Anda menetapkan nilai non-konstan ke sebuah variabel, TypeScript biasanya akan menyimpulkan tipe string yang lebih luas.

2. Inferensi Tipe Literal Angka:


const port = 8080; // TypeScript menyimpulkan tipe port sebagai 8080

function startServer(portNumber: 8080) {
  console.log(`Starting server on port ${portNumber}`);
}

startServer(port); // Valid

const anotherPort = 3000;
startServer(anotherPort); // Error: Argument of type 'number' is not assignable to parameter of type '8080'.

Menggunakan Tipe Literal dengan Tipe Kondisional

Tipe literal menjadi lebih kuat lagi ketika digabungkan dengan tipe kondisional. Tipe kondisional memungkinkan Anda mendefinisikan tipe yang bergantung pada tipe lain, menciptakan sistem tipe yang sangat fleksibel dan ekspresif.

Sintaksis Dasar

Sintaksis untuk tipe kondisional adalah:


TypeA extends TypeB ? TypeC : TypeD

Ini berarti: jika TypeA dapat ditetapkan ke TypeB, maka tipe yang dihasilkan adalah TypeC; jika tidak, tipe yang dihasilkan adalah TypeD.

Contoh Praktis

1. Memetakan Status ke Pesan:


type Status = "pending" | "in progress" | "completed" | "failed";

type StatusMessage = T extends "pending"
  ? "Waiting for action"
  : T extends "in progress"
  ? "Currently processing"
  : T extends "completed"
  ? "Task finished successfully"
  : "An error occurred";

function getStatusMessage(status: T): StatusMessage {
  switch (status) {
    case "pending":
      return "Waiting for action" as StatusMessage;
    case "in progress":
      return "Currently processing" as StatusMessage;
    case "completed":
      return "Task finished successfully" as StatusMessage;
    case "failed":
      return "An error occurred" as StatusMessage;
    default:
      throw new Error("Invalid status");
  }
}

console.log(getStatusMessage("pending"));    // Waiting for action
console.log(getStatusMessage("in progress")); // Currently processing
console.log(getStatusMessage("completed"));   // Task finished successfully
console.log(getStatusMessage("failed"));      // An error occurred

Contoh ini mendefinisikan tipe StatusMessage yang memetakan setiap status yang mungkin ke pesan yang sesuai menggunakan tipe kondisional. Fungsi getStatusMessage memanfaatkan tipe ini untuk menyediakan pesan status yang aman secara tipe (type-safe).

2. Membuat Penangan Event yang Aman Secara Tipe:


type EventType = "click" | "mouseover" | "keydown";

type EventData = T extends "click"
  ? { x: number; y: number; } // Data event klik
  : T extends "mouseover"
  ? { target: HTMLElement; }   // Data event mouseover
  : { key: string; }             // Data event keydown

function handleEvent(type: T, data: EventData) {
  console.log(`Handling event type ${type} with data:`, data);
}

handleEvent("click", { x: 10, y: 20 }); // Valid
handleEvent("mouseover", { target: document.getElementById("myElement")! }); // Valid
handleEvent("keydown", { key: "Enter" }); // Valid

handleEvent("click", { key: "Enter" }); // Error: Argument of type '{ key: string; }' is not assignable to parameter of type '{ x: number; y: number; }'.

Contoh ini membuat tipe EventData yang mendefinisikan struktur data yang berbeda berdasarkan jenis event. Ini memungkinkan Anda untuk memastikan bahwa data yang benar diteruskan ke fungsi handleEvent untuk setiap jenis event.

Praktik Terbaik Menggunakan Tipe Literal

Untuk menggunakan tipe literal secara efektif dalam proyek TypeScript Anda, pertimbangkan praktik terbaik berikut:

Manfaat Menggunakan Tipe Literal

Kesimpulan

Tipe literal TypeScript adalah fitur canggih yang memungkinkan Anda untuk menerapkan batasan nilai yang ketat, meningkatkan kejelasan kode, dan mencegah kesalahan. Dengan memahami sintaksis, penggunaan, dan manfaatnya, Anda dapat memanfaatkan tipe literal untuk membuat aplikasi TypeScript yang lebih tangguh dan mudah dipelihara. Mulai dari mendefinisikan palet warna dan endpoint API hingga menangani berbagai bahasa dan membuat penangan event yang aman secara tipe, tipe literal menawarkan berbagai aplikasi praktis yang dapat secara signifikan meningkatkan alur kerja pengembangan Anda.