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

Claude Code dan TypeScript: tips praktis agar cepat sekaligus aman

Gunakan strict, Zod, Union, generic, satisfies, dan type test untuk meningkatkan TypeScript dengan Claude Code.

Claude Code dan TypeScript: tips praktis agar cepat sekaligus aman

Claude Code bisa mempercepat pengembangan TypeScript, terutama untuk form, helper API, dan test. Namun jika batas tipe tidak jelas, kode rapuh juga bisa muncul dengan cepat. Untuk pemula, kebiasaan paling aman adalah menetapkan aturan tipe sebelum meminta fitur dibuat.

Di artikel ini, strict berarti TypeScript tidak mudah meloloskan kode mencurigakan. Tipe domain adalah aturan bisnis yang ditulis sebagai tipe. Discriminated Union memisahkan bentuk data berdasarkan status, dan validasi runtime mengecek data saat program berjalan.

Beri Peta Tipe Terlebih Dahulu

Sebelum prompt panjang, siapkan peta kecil: aturan compiler, tipe domain, input eksternal, state, dan type test. Peta ini membuat diff dari Claude Code lebih mudah ditinjau.

flowchart TD
  A["Kebutuhan"] --> B["tsconfig: aturan strict"]
  B --> C["Tipe domain: Plan dan Account"]
  C --> D["Data eksternal: unknown lalu validasi"]
  D --> E["State: discriminated union"]
  E --> F["Type test: expectTypeOf / tsd"]
  F --> G["Implementasi dan review dengan Claude Code"]

Gunakan dokumentasi resmi sebagai dasar: strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes, Narrowing, Generics, Utility Types, dan catatan satisfies. Untuk validasi runtime, lihat juga dokumentasi Zod.

Bacaan terkait: TypeScript Utility Types, TypeScript Generics, dan Zod Validation.

Mulai dari tsconfig strict

Jangan hanya meminta “buat dengan TypeScript”. Beri Claude Code kontrak compiler terlebih dahulu.

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitOverride": true,
    "noFallthroughCasesInSwitch": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*.ts", "src/**/*.tsx", "tests/**/*.ts"]
}

Masukkan juga batasan di prompt.

Repository ini memakai strict TypeScript.
Jangan menambahkan any. Input eksternal diterima sebagai unknown dan divalidasi dengan Zod.
Saat menangani Union di switch, tambahkan never exhaustiveness check.
Setelah implementasi, jalankan npx tsc --noEmit.

noUncheckedIndexedAccess membuat kemungkinan undefined tetap terlihat saat membaca array atau object. Ini membantu menangkap field API yang hilang, list kosong, dan terjemahan CMS yang belum lengkap.

Use Case 1: Modelkan Plan SaaS sebagai Tipe Domain

Tipe domain adalah aturan bisnis dalam TypeScript. Plan, permission, billing state, dan publish state sebaiknya ada sebelum UI.

export type Plan = "free" | "pro" | "enterprise";

export type Account = {
  id: string;
  email: string;
  plan: Plan;
  seats: number;
  trialEndsAt: string | null;
};

export type CreateAccountInput = {
  email: string;
  plan: Exclude<Plan, "enterprise">;
  seats?: number;
};

export type UpdateAccountInput = Partial<
  Pick<Account, "email" | "plan" | "seats" | "trialEndsAt">
>;

Exclude menghapus anggota dari Union. Partial membuat property menjadi opsional. Cocok untuk update API, tetapi berbahaya untuk create input jika field wajib ikut menjadi opsional.

Use Case 2: Validasi Data API dari unknown

Tipe TypeScript hilang saat runtime. API, form, cookie, localStorage, CSV, dan output AI bisa rusak. Terima sebagai unknown, validasi, lalu gunakan nilai yang sudah bertipe.

npm install zod
import { z } from "zod";

const AccountSchema = z.object({
  id: z.string().min(1),
  email: z.string().email(),
  plan: z.enum(["free", "pro", "enterprise"]),
  seats: z.number().int().positive(),
  trialEndsAt: z.string().datetime().nullable()
});

type Account = z.infer<typeof AccountSchema>;

export function parseAccountResponse(json: unknown): Account {
  return AccountSchema.parse(json);
}

unknown berarti nilainya belum terbukti. Berbeda dari any, ia memaksa validasi sebelum property dibaca.

Use Case 3: Tutup State Payment dengan Union

Payment, upload, submit form, dan background job adalah state machine. status: string terlalu longgar.

type PaymentResult =
  | { status: "pending"; invoiceId: string }
  | { status: "paid"; invoiceId: string; paidAt: string }
  | { status: "failed"; invoiceId: string; reason: string };

export function renderPaymentMessage(result: PaymentResult): string {
  switch (result.status) {
    case "pending":
      return `Invoice ${result.invoiceId} is waiting for payment.`;
    case "paid":
      return `Invoice ${result.invoiceId} was paid at ${result.paidAt}.`;
    case "failed":
      return `Invoice ${result.invoiceId} failed: ${result.reason}.`;
    default: {
      const exhaustive: never = result;
      return exhaustive;
    }
  }
}

Cabang never membuat TypeScript memeriksa semua kasus valid. Jika status refunded ditambahkan nanti, compiler akan meminta cabang baru.

Use Case 4: Generic dan satisfies

Generic membuat helper bisa dipakai ulang tanpa kehilangan tipe spesifik di tempat pemanggilan.

export function groupBy<T, K extends PropertyKey>(
  items: readonly T[],
  getKey: (item: T) => K
): Partial<Record<K, T[]>> {
  const grouped: Partial<Record<K, T[]>> = {};

  for (const item of items) {
    const key = getKey(item);
    const bucket = grouped[key] ?? [];
    bucket.push(item);
    grouped[key] = bucket;
  }

  return grouped;
}

const accounts = [
  { id: "a1", plan: "free" },
  { id: "a2", plan: "pro" },
  { id: "a3", plan: "pro" }
] as const;

const byPlan = groupBy(accounts, (account) => account.plan);
const proAccounts = byPlan.pro ?? [];

console.log(proAccounts.map((account) => account.id));

Untuk object konfigurasi, satisfies biasanya lebih baik daripada assertion yang terlalu luas.

type ApiRoute = {
  method: "GET" | "POST" | "PATCH" | "DELETE";
  path: `/${string}`;
  auth: boolean;
};

const routes = {
  listAccounts: { method: "GET", path: "/accounts", auth: true },
  createAccount: { method: "POST", path: "/accounts", auth: true },
  healthCheck: { method: "GET", path: "/health", auth: false }
} as const satisfies Record<string, ApiRoute>;

type RouteName = keyof typeof routes;

export function getRoute(name: RouteName) {
  return routes[name];
}

Tambahkan Type-Level Tests

Tipe publik yang penting perlu diuji.

npm install -D vitest tsd
import { expectTypeOf, test } from "vitest";

type CreateAccountInput = {
  email: string;
  plan: "free" | "pro";
  seats?: number;
};

test("CreateAccountInput keeps the public API narrow", () => {
  expectTypeOf<CreateAccountInput>().toMatchTypeOf<{
    email: string;
    plan: "free" | "pro";
    seats?: number;
  }>();
});

Use Case Nyata dan Pitfall Umum

Use caseYang dikunci type systemYang bisa dibuat Claude Code
Billing SaaSplan, invoice state, permissioncabang UI, form, pesan
Layar admin APIZod schema, response typefetch helper, tabel, loading state
CMS artikelslug, locale, publish state, hero imagedraft MDX, halaman list, validasi
Contact forminput schema, result UnionUI, submit handler, Vitest
JebakanDampakSolusi
Response API sebagai anyJSON rusak tetap compileunknown dan Zod
status: stringstate mustahil bisa masukdiscriminated Union
Banyak as Usererror disembunyikanschema, guard, satisfies
Partial<T> untuk createfield wajib menjadi opsionalpisahkan create dan update
Tidak ada type testtipe publik melebar diam-diamexpectTypeOf atau tsd

Di workflow artikel ClaudeCodeLab, Masa pernah melihat URL locale salah karena lang bertipe string. Setelah locale dibuat sebagai Union tertutup, saran perubahan dari Claude Code menjadi jauh lebih stabil.

Aturan dan CTA

## TypeScript rules
- Use strict TypeScript.
- Do not introduce `any`. Use `unknown` at external boundaries.
- Prefer discriminated unions for states.
- Prefer `satisfies` over broad type assertions.
- Derive API types from Zod schemas when runtime data is involved.
- Add Vitest or tsd style type checks for exported helper types.
- Run `npx tsc --noEmit` before reporting completion.

Untuk proyek solo, katalog produk berisi template dan checklist workflow Claude Code. Untuk tim, training dan konsultasi Claude Code dapat membantu migrasi strict, batas Zod, type tests, dan aturan CLAUDE.md di repository nyata.

Hasil Uji

Saya mencoba alur ini di proyek TypeScript kecil: response API diganti dari any menjadi unknown plus Zod, lalu Claude Code diminta menambahkan cabang Union dan test expectTypeOf. Hasil yang berguna adalah state yang belum ditangani dan akses property yang tidak ada ditemukan sebelum review.

#Claude Code #TypeScript #type safety #development efficiency #frontend
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.