Tips & Tricks (Diperbarui: 2/6/2026)

Utility Types TypeScript dengan Claude Code: panduan praktis

Pelajari Pick, Omit, Partial, Record, ReturnType, dan Awaited lewat contoh Claude Code yang bisa dijalankan.

Utility Types TypeScript dengan Claude Code: panduan praktis

Utility types TypeScript adalah alat untuk mengubah tipe yang sudah ada menjadi tipe lain untuk kebutuhan tertentu. Jika kamu menyalin tipe User secara manual menjadi tipe tampilan publik, input form, dan update API, salinan itu akan mudah tidak sinkron ketika field berubah. Claude Code bisa membantu refactor, tetapi kamu tetap perlu memahami maksud setiap utility type agar bisa mereview hasilnya.

Panduan ini menjelaskan Pick, Omit, Partial, Required, Readonly, Record, ReturnType, dan Awaited dengan bahasa yang ramah pemula. Setelah itu ada contoh yang bisa langsung disalin dan dijalankan untuk menunjukkan cara meminta Claude Code membuat desain tipe yang praktis. Gunakan TypeScript Handbook Utility Types sebagai referensi resmi, dan pasangkan dengan TSConfig strict untuk menemukan kesalahan lebih cepat.

Untuk bacaan terkait di ClaudeCodeLab, lihat tips TypeScript praktis dan panduan generics TypeScript.

Model sederhana

Utility type adalah cara aman untuk “menyalin dan membentuk ulang” tipe. Bayangkan kamu menduplikasi spreadsheet, menghapus beberapa kolom, membuat beberapa kolom opsional, lalu memakai versi itu di alur lain. Bedanya, TypeScript memeriksa bentuk baru tersebut sebelum kode dijalankan.

Pick<User, "id" | "name"> mengambil hanya id dan name dari User. Omit<User, "passwordHash"> mempertahankan hampir semua field, tetapi menghapus passwordHash. Keduanya mirip, tetapi arah bacanya berbeda. Gunakan Pick ketika tipe tujuan memang kecil, dan Omit ketika tipe tujuan hampir sama dengan sumbernya tetapi beberapa field berbahaya harus dikeluarkan.

Partial<User> membuat semua properti menjadi opsional. Ini cocok untuk draft, PATCH, dan form yang belum selesai, tetapi bisa membuat email yang wajib saat pembuatan akun menjadi opsional. Required<User> melakukan kebalikannya, yaitu membuat properti opsional menjadi wajib. Readonly<User> mencegah reassignment dan cocok untuk konfigurasi atau data master.

Record<Keys, Type> membuat dictionary dengan key yang diketahui. ReturnType<typeof fn> mengambil tipe return dari sebuah fungsi. Awaited<Promise<T>> mengambil tipe nilai setelah await. Gabungan Awaited<ReturnType<typeof fetchSomething>> menjaga tipe API dan UI tetap sinkron.

flowchart LR
  A["Source type: User"] --> B["Pick: public view"]
  A --> C["Omit: remove secrets"]
  A --> D["Partial: update input"]
  A --> E["Required: validated input"]
  A --> F["Readonly: fixed settings"]
  G["Function"] --> H["ReturnType"]
  I["Promise"] --> J["Awaited"]

Perbandingan cepat

TypeFungsiPenggunaan praktisHati-hati
Pick<T, K>Memilih key tertentuList, profil publik, kartuKey yang tidak dipilih tidak tersedia
Omit<T, K>Menghapus key tertentuInput create, output publik, logTidak menghapus nilai runtime
Partial<T>Membuat semua key opsionalDraft, PATCH, form sebagianHanya shallow
Required<T>Membuat semua key wajibData tervalidasi sebelum simpanBisa terlalu ketat
Readonly<T>Mencegah reassignmentSetting, permission, konstantaObject nested perlu perlakuan tambahan
Record<K, T>Membuat dictionary key tetapPermission role, label, hargaRecord<string, T> sering terlalu luas
ReturnType<T>Mengambil return type fungsiSinkronisasi API dan UIGunakan typeof functionName
Awaited<T>Mengambil nilai Promise yang resolvedHasil fungsi asyncBukan runtime await

Tempel tabel ini ke Claude Code dan minta review apakah setiap utility type sesuai dengan tujuannya. Pada proyek dengan strict: true, tipe yang samar akan gagal lebih cepat, dan itu membantu mencegah bug.

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "skipLibCheck": true
  }
}

Use case 1: membuat tipe UI dan form dari User

Layar admin sering membutuhkan beberapa versi dari entity yang sama. Tipe database, tipe publik, dan tipe input form sebaiknya tidak dipelihara sebagai salinan manual. Simpan satu tipe sumber, lalu turunkan sisanya dengan utility types.

type UserRole = "admin" | "editor" | "viewer";

interface User {
  id: string;
  name: string;
  email: string;
  role: UserRole;
  bio: string;
  passwordHash: string;
  createdAt: Date;
  updatedAt: Date;
}

type PublicUser = Pick<User, "id" | "name" | "role" | "bio">;
type UserDraft = Partial<Omit<User, "id" | "passwordHash" | "createdAt" | "updatedAt">>;

type CreateUserInput =
  Required<Pick<User, "name" | "email" | "role">> &
  Partial<Pick<User, "bio">>;

function buildCreatePayload(input: CreateUserInput): Omit<User, "id" | "createdAt" | "updatedAt"> {
  return {
    name: input.name,
    email: input.email,
    role: input.role,
    bio: input.bio ?? "",
    passwordHash: "hashed-by-server",
  };
}

const publicUser: PublicUser = {
  id: "u_001",
  name: "Masa",
  role: "admin",
  bio: "Claude Code workflow designer",
};

const draft: UserDraft = {
  name: "Draft user",
  bio: "Saved before email is confirmed",
};

console.log(publicUser);
console.log(buildCreatePayload({ name: "Aki", email: "aki@example.com", role: "editor" }));
console.log(draft);

Prompt untuk Claude Code sebaiknya memisahkan apa yang disimpan, apa yang dihapus, dan kapan sebuah field wajib.

Create public display, form draft, and create-API types from the User type.
Do not expose passwordHash.
For creation, require only name, email, and role. Keep bio optional.
Use Pick/Omit/Partial/Required and briefly explain why each utility type is used.

Di produksi, gabungkan tipe ini dengan validasi runtime seperti Zod. Utility types memeriksa bentuk sebelum kode berjalan; ia tidak membuktikan bahwa data kiriman user benar-benar valid.

Use case 2: mengunci fitur plan dengan Record

Plan harga, role, dan tabel status sangat cocok dengan Record. Compiler bisa memberi tahu jika plan team belum diisi atau prioritySupport salah ketik.

type Plan = "free" | "pro" | "team";
type Feature = "exportPdf" | "inviteMember" | "prioritySupport";

const featureMatrix: Readonly<Record<Plan, Readonly<Record<Feature, boolean>>>> = {
  free: {
    exportPdf: false,
    inviteMember: false,
    prioritySupport: false,
  },
  pro: {
    exportPdf: true,
    inviteMember: false,
    prioritySupport: false,
  },
  team: {
    exportPdf: true,
    inviteMember: true,
    prioritySupport: true,
  },
};

function canUse(plan: Plan, feature: Feature): boolean {
  return featureMatrix[plan][feature];
}

console.log(canUse("pro", "exportPdf"));
console.log(canUse("free", "prioritySupport"));

Readonly menandakan bahwa matrix ini tidak boleh dimutasi saat runtime. Karena sifatnya shallow, contoh ini juga memberi Readonly pada Record bagian dalam. Untuk data yang lebih dalam, pertimbangkan as const atau helper deep readonly milik proyek.

Use case 3: memakai ulang hasil API dengan ReturnType dan Awaited

Jika tipe API client dan tipe UI ditulis terpisah, perubahan response menjadi mahal. ReturnType dan Awaited mengambil tipe langsung dari fungsi async yang sebenarnya.

async function fetchInvoice(invoiceId: string) {
  return {
    id: invoiceId,
    status: "paid" as const,
    amount: 48000,
    currency: "JPY" as const,
    paidAt: new Date("2026-06-02T10:00:00+09:00"),
  };
}

type Invoice = Awaited<ReturnType<typeof fetchInvoice>>;
type InvoiceSummary = Pick<Invoice, "id" | "status" | "amount" | "currency">;

function formatInvoice(invoice: InvoiceSummary): string {
  return `${invoice.id}: ${invoice.amount.toLocaleString()} ${invoice.currency} (${invoice.status})`;
}

async function main() {
  const invoice = await fetchInvoice("inv_20260602");
  console.log(formatInvoice(invoice));
}

main();

Jika fungsi API adalah batas yang dipercaya, minta Claude Code untuk tidak menulis tipe response duplikat secara manual. Jika batasnya adalah API eksternal atau input user, tetap tambahkan validasi runtime dan penanganan error.

Use case 4: memakai Partial pada level yang tepat

Partial<T> bersifat shallow. Ia tidak otomatis membuat field di dalam object nested menjadi opsional, dan ini sering membuat pemula bingung.

interface Profile {
  id: string;
  displayName: string;
  settings: {
    emailNotification: boolean;
    smsNotification: boolean;
  };
}

type ProfilePatch =
  Omit<Partial<Profile>, "settings"> & {
    settings?: Partial<Profile["settings"]>;
  };

function patchProfile(current: Profile, patch: ProfilePatch): Profile {
  return {
    ...current,
    ...patch,
    settings: {
      ...current.settings,
      ...patch.settings,
    },
  };
}

const profile: Profile = {
  id: "p_001",
  displayName: "Masa",
  settings: {
    emailNotification: true,
    smsNotification: false,
  },
};

console.log(patchProfile(profile, { settings: { smsNotification: true } }));

Jika hanya memakai ProfilePatch = Partial<Profile>, update settings tetap membutuhkan object settings lengkap. Untuk tim yang berisi pemula, tipe konkret seperti ini sering lebih mudah dirawat daripada generic deep partial yang terlalu pintar.

Kegagalan yang perlu dihindari

Omit menghapus key dari tipe, bukan dari object runtime. Jika kamu mengembalikan log atau response publik, nilai rahasia harus benar-benar dibuang.

interface Account {
  id: string;
  email: string;
  passwordHash: string;
}

type SafeAccount = Omit<Account, "passwordHash">;

function toSafeAccount(account: Account): SafeAccount {
  const { passwordHash, ...safeAccount } = account;
  return safeAccount;
}

console.log(toSafeAccount({
  id: "a_001",
  email: "masa@example.com",
  passwordHash: "secret",
}));

Record<string, T> biasanya terlalu luas untuk aturan bisnis. Jika key yang valid sudah diketahui, pakai union seperti type Plan = "free" | "pro" | "team".

Required<T> yang dipakai terlalu awal dapat membuat form terasa berat. Gunakan setelah validasi, ketika data memang sudah lengkap sebelum disimpan.

Awaited<T> hanya menjelaskan tipe Promise yang sudah resolved. Ia tidak menunggu di runtime; kamu tetap perlu await atau .then(). Jika perbedaan ini kabur, Claude Code bisa merapikan tipe tetapi lupa loading dan error state.

Prompt review untuk Claude Code

Setelah implementasi, minta review yang fokus pada risiko, bukan hanya cleanup.

Review this TypeScript type design.
1. Are Pick/Omit/Partial/Required/Readonly/Record matched to their use cases?
2. Are secrets removed at runtime, not only with Omit?
3. Does Partial make create inputs too loose?
4. Do ReturnType and Awaited reduce duplicated API types?
5. Are any vague any or broad string types left under strict settings?

Format ini mengarahkan Claude Code untuk mencegah defect. Masa pernah mengalami masalah ini di layar admin kecil: memakai Partial di mana-mana membuat email kosong terlihat boleh sampai tahap simpan. Setelah memisahkan “draft”, “create”, dan “saved” menjadi tipe turunan yang berbeda, patch dari Claude Code jauh lebih mudah direview.

Kesimpulan

Utility types TypeScript bukan akrobat tipe. Ia adalah alat praktis untuk mengekspresikan perbedaan antara data publik, draft, input tervalidasi, konfigurasi tetap, dan hasil API async tanpa menduplikasi definisi. Gunakan Pick dan Omit untuk mengontrol field, Partial dan Required untuk memodelkan tahap workflow, Readonly dan Record untuk melindungi konfigurasi, serta ReturnType plus Awaited untuk mengurangi tipe API duplikat.

ClaudeCodeLab membantu tim menerapkan Claude Code pada arsitektur TypeScript, CMS konten, internal tools, dan funnel monetisasi. Jika proyekmu sudah memakai tipe tetapi masih sering mengirim kesalahan yang seharusnya bisa dicegah, ajukan training dan konsultasi Claude Code.

Saya memeriksa contoh di artikel ini dengan pola pikir TypeScript strict. Manfaat paling terasa datang dari ReturnType dan Awaited: ketika response API berubah, bagian UI yang perlu disesuaikan muncul lebih cepat. Catatan pentingnya adalah Omit: ia tidak pernah menghapus rahasia di runtime, jadi fungsi response publik tetap harus membuang properti secara eksplisit.

#Claude Code #TypeScript #utility types #type safety #kualitas kode
Gratis

PDF gratis: cheatsheet Claude Code

Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.

Kami menjaga datamu dan tidak mengirim spam.

Masa

Tentang penulis

Masa

Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.