Tidak suka iklan? Pergi Bebas Iklan Hari ini

Tipe Berguna TypeScript — Yang Perlu Dihafal (dan yang Lainnya)

Diperbarui pada

Sebagian, Pilih, Sisakan, Catat, ReturnType, Parameter, Awaited — delapan tipe utilitas dengan contoh sebelum dan sesudah serta penilaian jujur mengenai tipe-tipe mana yang akan Anda gunakan setiap minggu.

Tipe TypeScript Utility — Yang Perlu Diingat (dan yang Lainnya) 1
IKLAN · HAPUS?

Anda mungkin sudah membaca halaman dokumentasi TypeScript yang menyebutkan lebih dari 20 tipe utilitas dalam urutan alfabetis, mendefinisikan setiap tipe dalam satu kalimat, dan membuat Anda bertanya-tanya mana yang seharusnya dikuasai secara internal dan mana yang hanya digunakan oleh orang yang menulis bagian internal kerangka kerja. Ini adalah versi yang lebih jujur: delapan tipe, contoh sebelum dan sesudah yang konkret, serta jawaban langsung mengenai penggunaan harian dibandingkan penggunaan yang jarang terjadi.

Jika Anda mulai dari JSON murni dan belum menulis antarmuka Anda, maka pembuat konversi JSON ke TypeScript akan membawa Anda ke antarmuka dasar dalam beberapa detik — lalu kembali ke sini untuk menambahkan tipe utilitas ini.

Partial — Penggunaan mingguan. Pelajari terlebih dahulu.

Membuat setiap properti di T opsional. Kasus penggunaan klasik adalah pembaruan berbentuk PATCH, di mana Anda hanya mengirimkan bidang-bidang yang berubah.

// Before: manually duplicating the interface with optional versions of everything
interface UserUpdate {
  name?: string;
  email?: string;
  age?: number;
}

// After: Partial derives it from the source of truth
interface User {
  name: string;
  email: string;
  age: number;
}

function updateUser(id: string, updates: Partial<User>) {
  // updates.name is string | undefined — TypeScript knows
}

Masalah pada versi "sebelumnya": jika Anda menambahkan sebuah properti ke User, Anda harus mengingat untuk menambahkannya ke UserUpdate pula. Dengan Partial<User>, menambahkan sebuah properti ke antarmuka sumber secara otomatis membuatnya tersedia dalam pembaruan. Tipe-tipe tetap sinkron.

Required — Penggunaan bulanan, tetapi menghemat Anda dalam satu skenario tertentu.

Kebalikan dari Partial — menghapus semua ? dari suatu tipe. Paling berguna setelah validasi: Anda menerima objek konfigurasi yang tidak terstruktur, memverifikasi bahwa semua bidang ada, dan kemudian ingin TypeScript berhenti menyarankan bahwa semua nilai mungkin tidak terisi.

// Before: after validating, you still fight undefined checks everywhere
interface Config {
  apiUrl?: string;
  timeout?: number;
  retries?: number;
}

function initApp(config: Config) {
  config.apiUrl?.trim(); // need optional chaining even after checking
}

// After: Required signals "this has been validated, stop warning me"
function validateAndInit(raw: Config): void {
  if (!raw.apiUrl) throw new Error("apiUrl required");
  if (!raw.timeout) throw new Error("timeout required");
  if (!raw.retries) throw new Error("retries required");

  const config = raw as Required<Config>;
  config.apiUrl.trim(); // no optional chaining needed
}

Catatan yang jujur: Required<T> tidak melakukan validasi di runtime — Anda tetap harus menulis pengecekan tersebut. Ini hanya mengubah apa yang diyakini TypeScript di level tipe. Gunakan ini untuk menyampaikan “ini telah divalidasi” pada tingkat tipe.

Pick — Penggunaan mingguan. Pilih apa yang Anda inginkan.

Membuat tipe baru yang hanya mengandung properti yang Anda sebutkan dari T. Berguna ketika Anda ingin subset dari antarmuka yang lebih besar — bentuk API publik, model tampilan, respons terstruktur.

// Before: manually maintaining a separate interface that drifts from User
interface UserPublicProfile {
  name: string;
  bio: string;
  avatarUrl: string;
}

// After: Pick stays in sync with the source
interface User {
  id: string;
  name: string;
  email: string;       // private — shouldn't go to clients
  passwordHash: string;  // definitely private
  bio: string;
  avatarUrl: string;
}

type UserPublicProfile = Pick<User, "name" | "bio" | "avatarUrl">;

Pick adalah alat yang tepat ketika daftar properti yang Anda inginkan lebih pendek daripada daftar yang Anda butuhkan untuk exclude. Ketika arahnya terbalik, gunakan Omit.

Omit — Penggunaan mingguan. Pilih apa yang tidak Anda inginkan.

Kompresi dari Pick — membuat tipe yang memiliki semua properti dari T kecuali yang Anda sebutkan. Ketika Anda memiliki antarmuka besar dan hanya beberapa bidang yang perlu dihapus, Omit membuat niat lebih jelas daripada menyebutkan semua yang Anda inginkan.

// Before: manually listing every field except the sensitive ones
// Easy to miss a new field added to User later
interface UserForClient {
  id: string;
  name: string;
  email: string;
  bio: string;
  avatarUrl: string;
  // passwordHash intentionally omitted — but a future dev might not know that
}

// After: Omit states the exclusion explicitly
type UserForClient = Omit<User, "passwordHash">;
// If User gains a new "address" field, UserForClient gets it automatically

Satu hal yang tidak dilakukan oleh Pick atau Omit: mengubah tipe properti individu. Jika Anda ingin memilih bidang-bidang dan dan mengubah tipe mereka, Anda sedang mencari tipe pemetaan, yang merupakan alat yang berbeda secara utuh.

Record — Penggunaan mingguan. Dikatakan sebagai kamus terstruktur.

Membuat tipe objek di mana semua kunci berjenis K dan semua nilai berjenis V. Manfaat nyata muncul ketika K adalah tipe gabungan — TypeScript akan memberi kesalahan jika Anda melewatkan kunci. Tanda indeks umum secara diam-diam menerima objek yang tidak lengkap; Record dengan kunci gabungan tidak.

// Before: index signature with no exhaustiveness checking
const rolePermissions: { [key: string]: string[] } = {
  admin: ["read", "write", "delete"],
  editor: ["read", "write"],
  // forgot "viewer" — TypeScript doesn't notice
};

// After: Record with a union key enforces completeness
type Role = "admin" | "editor" | "viewer";

const rolePermissions: Record<Role, string[]> = {
  admin: ["read", "write", "delete"],
  editor: ["read", "write"],
  viewer: ["read"],
  // TypeScript error if any Role is missing
  // TypeScript error if you try to add an unknown key
};

Pemeriksaan keberadaan adalah manfaat utama. Ini penting ketika kunci-kunci tersebut mewakili sesuatu yang bermakna seperti peran, jenis acara, metode HTTP, atau kode status — apa pun yang di mana kehilangan kasus merupakan bug, bukan hanya kekurangan.

ReturnType — Penggunaan mingguan jika Anda menggunakan fungsi dari pihak ketiga.

Mengambil tipe hasil dari fungsi. Paling berguna ketika Anda menggunakan fungsi dari library dan ingin mengetik variabel yang menyimpan hasilnya — tanpa harus mengimpor tipe hasil secara terpisah (yang tidak selalu diekspor oleh library).

// Before: manually annotating the return type — it can drift
function getCurrentUser() {
  return { id: "u1", name: "Alice", roles: ["admin"] as const };
}

let cachedUser: { id: string; name: string; roles: readonly string[] };
// If getCurrentUser's return changes, this annotation doesn't update automatically

// After: ReturnType derives it and keeps in sync
type CurrentUser = ReturnType<typeof getCurrentUser>;
let cachedUser: CurrentUser;

// Especially useful with unexported library types:
import { useQuery } from "@tanstack/react-query";
type QueryResult = ReturnType<typeof useQuery>;
// No need to figure out what QueryObserverResult<...> looks like or import it

Parameters — Penggunaan yang jarang terjadi. Terutama untuk wrapper.

Mengambil tipe parameter dari fungsi sebagai tuple. Pendekatan yang jujur: Anda tidak akan menggunakan ini setiap minggu kecuali Anda menulis banyak fungsi wrapper atau utilitas tingkat tinggi. Namun, ketika Anda membutuhkannya, tidak ada alternatif yang bersih.

// Before: manually duplicating parameter types in the wrapper
function createEvent(
  type: string,
  payload: Record<string, unknown>,
  timestamp: number
) { /* ... */ }

// Every time createEvent changes, this wrapper needs a manual update:
function loggedCreateEvent(
  type: string,
  payload: Record<string, unknown>,
  timestamp: number
) {
  console.log("Creating event:", type);
  return createEvent(type, payload, timestamp);
}

// After: Parameters keeps the wrapper in sync automatically
function loggedCreateEvent(...args: Parameters<typeof createEvent>) {
  console.log("Creating event:", args[0]);
  return createEvent(...args);
}

Pola penyebaran + sisa (...args: Parameters<F>) adalah idioma utama. Pola ini juga muncul dalam mock pengujian: jest.fn<ReturnType<F>, Parameters<F>>() memberi Anda mock yang sepenuhnya terstruktur tanpa harus mengimpor tanda tangan fungsi secara terpisah.

Awaited — Gunakan ketika Anda perlu menghapus tipe Promise tanpa memanggilnya.

Ditambahkan di TypeScript 4.5 (diluncurkan November 2021). Menghapus secara rekursif Promise<T> untuk mencapai T. Sebelum tersedia, mendapatkan tipe yang terurai dari fungsi asink berarti menulis tipe kondisional: ReturnType<F> extends Promise<infer U> ? U : never — yang gagal pada Promise yang dilapis dua kali dan tidak nyaman dibaca dalam perbedaan.

// Before: conditional type to unwrap — breaks on nested Promises
async function fetchUser(id: string) {
  const res = await fetch(`/api/users/${id}`);
  return res.json() as { id: string; name: string; email: string };
}

type FetchedUser =
  ReturnType<typeof fetchUser> extends Promise<infer U> ? U : never;
// Works for Promise<T> but not Promise<Promise<T>>

// After: Awaited handles both cases cleanly
type FetchedUser = Awaited<ReturnType<typeof fetchUser>>;

// Awaited recursively unwraps:
type A = Awaited<Promise<string>>;            // string
type B = Awaited<Promise<Promise<string>>>;  // string
type C = Awaited<string>;                      // string (no-op on non-Promise)

Kombinasi Awaited<ReturnType<typeof fn>> sangat perlu dihafal sebagai satu unit — ini mencakup 90% ekstraksi tipe asink dalam kode aplikasi.

Peta cepat

Tipe utilitasApa yang dilakukanFrekuensi penggunaanKasus penggunaan utama
Partial<T>Semua properti menjadi opsionalMingguanPayload pembaruan PATCH
Required<T>Semua properti opsional menjadi wajibBulananSinyal “ini lengkap” setelah validasi
Pick<T, K>Hanya mempertahankan properti yang disebutkanMingguanBentuk API publik, model tampilan
Omit<T, K>Menghapus properti yang disebutkanMingguanMenghilangkan bidang sensitif atau tidak relevan
Record<K, V>Objek dengan kunci berjenis K dan nilai berjenis VMingguanKamus terstruktur dengan pemeriksaan keberadaan
ReturnType<F>Mengambil tipe hasil fungsiMingguan (terutama dengan library)Mengetik variabel dari tipe yang diperkirakan atau tidak diekspor
Parameters<F>Mengambil tipe parameter fungsi sebagai tupleJarang terjadiFungsi wrapper, mock terstruktur
Awaited<T>Menghapus secara rekursif tipe PromiseJarang terjadiMendapatkan tipe yang terurai dari fungsi asink

Yang tidak ada di daftar ini

TypeScript juga menyediakan Readonly<T>, NonNullable<T>, Extract<T,U>, Exclude<T,U>, InstanceType<C>, ConstructorParameters<C>, dan beberapa tipe literal template. Mereka benar-benar ada dan kadang-kadang berguna, tetapi muncul dalam kode library, utilitas umum, dan pemrograman tingkat tipe — bukan dalam logika aplikasi biasa. Jika Anda telah menghafal delapan tipe di atas, cari tahu yang lain saat Anda benar-benar membutuhkannya.

Prinsip dasar yang sama berlaku untuk semua dari mereka: menggambarkan transformasi dari tipe yang ada, bukan menggandakan tipe tersebut. Nama-nama tipe utilitas tersebut hanyalah kosakata untuk menyampaikan niat tersebut kepada TypeScript.

Ingin bebas iklan? Bebas Iklan Hari Ini

Instal Ekstensi Kami

Tambahkan alat IO ke browser favorit Anda untuk akses instan dan pencarian lebih cepat

Ke Ekstensi Chrome Ke Ekstensi Tepi Ke Ekstensi Firefox Ke Ekstensi Opera

Papan Skor Telah Tiba!

Papan Skor adalah cara yang menyenangkan untuk melacak permainan Anda, semua data disimpan di browser Anda. Lebih banyak fitur akan segera hadir!

IKLAN · HAPUS?
IKLAN · HAPUS?
IKLAN · HAPUS?

Pojok Berita dengan Sorotan Teknologi

Terlibat

Bantu kami untuk terus menyediakan alat gratis yang berharga

Belikan aku kopi
IKLAN · HAPUS?