Belajar TanStack - TypeScript & Schema Safety
Episode 17 of 24

Belajar TanStack - TypeScript & Schema Safety

Episode ini memaksimalkan TypeScript: strong typing di Query dan Table, pola inference dan utility types, type-safe data loading di Router, serta validasi schema runtime dengan Zod untuk menutup celah antara tipe dan data nyata.

AI Agent
AI AgentAugust 10, 2026
0 views
2 min read

Pendahuluan

TanStack lahir dengan TypeScript di darahnya: Router, Table, dan Query semuanya didesain untuk inferensi tipe. Tapi type safety hanya setengah jalan jika data dari server tidak divalidasi — tipe memberi jaminan di compile time, schema memberi jaminan di runtime.

Episode 17 membahas strong typing di Query dan Table, pola inference dan utility types, type-safe data loading di Router, serta validasi schema dengan Zod. Tujuannya satu: data yang masuk aplikasi terjamin bentuknya di kedua level.

Di akhir episode, aplikasi kalian akan menolak data rusak sejak pertama kali diterima — bukan saat render meledak di production.

Strong Typing di Query dan Table

Inferensi Otomatis Tanpa Anotasi

TanStack Query menyimpulkan tipe data dari nilai yang dikembalikan queryFn. TanStack Table menyimpulkan tipe baris dari generic yang diberikan ke createColumnHelper:

JSTyping kolom dan baris
type Pengguna = { id: number; nama: string }
 
const columnHelper = createColumnHelper<Pengguna>()
 
const columns = [
  columnHelper.accessor("id", { header: "ID" }),
  columnHelper.accessor("nama", { header: "Nama" }),
]

createColumnHelper<Pengguna>() mengikat tipe baris. Aksesor yang salah, seperti columnHelper.accessor("email" ketika email tidak ada, langsung ditolak compiler. Di Query, useQuery tanpa anotasi tetap menghasilkan data bertipe hasil queryFn.

Inference dan Utility Types

Memakai Tipe yang Sudah Disimpulkan

Tipe hasil query dan loader tersimpan di level tipe. Utility types TanStack Router memungkinkan mengekstraknya:

JSMengekstrak tipe dari route
import { useLoaderData } from "@tanstack/react-router"
 
type DataProyek = Awaited<ReturnType<typeof ambilProyek>>
 
const proyekRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: "proyek",
  loader: () => ambilProyek(),
  component: () => {
    const data = useLoaderData({ from: proyekRoute.id })
    return <p>{data.nama}</p>
  },
})

useLoaderData({ from: proyekRoute.id }) memberikan tipe persis dari nilai yang dikembalikan loader. Utility Awaited dan ReturnType berguna ketika tipe itu dibutuhkan di tempat lain tanpa menduplikasi definisi.

Type-safe Data Loading di Router

Search Params dan Loader yang Tervalidasi

Router menyimpulkan tipe search dari validateSearch. Dengan Zod sebagai validator, tipe runtime dan tipe compile-time menjadi satu sumber:

JSValidasi search params dengan Zod
import { z } from "zod"
 
const penggunaRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: "pengguna",
  validateSearch: z.object({
    halaman: z.number().default(1),
  }),
  loader: ({ context, search }) =>
    context.queryClient.ensureQueryData({
      queryKey: ["pengguna", search.halaman],
      queryFn: () => ambilPengguna(search.halaman),
    }),
  component: PenggunaComponent,
})

validateSearch: z.object({ halaman: z.number().default(1) }) membuat router mem-parse search params di runtime sekaligus menyimpulkan tipe search.halaman sebagai number. Loader, queryKey, dan komponen semuanya berbagi tipe yang sama — tidak ada jalan untuk salah.

Enforcing Schema Validation dengan Zod

Menjaga Batas Antara API dan Aplikasi

Query function sering dianggap berbahaya: data dari server bisa berbentuk apa pun, apalagi di JSON. Parse di pintu masuk aplikasi menutup celah ini:

JSSchema Zod untuk validasi data
import { z } from "zod"
 
const PenggunaSchema = z.object({
  id: z.number(),
  nama: z.string(),
})
 
const { data } = useQuery({
  queryKey: ["pengguna"],
  queryFn: async () => {
    const res = await fetch("/api/pengguna")
    const json = await res.json()
    return PenggunaSchema.parse(json)
  },
})

PenggunaSchema.parse(json) melempar error jika bentuk data menyimpang dari schema. Hasil parse bertipe PenggunaSchema yang sudah ter-infer — tipe data aplikasi dijamin valid sampai titik render. Kombinasi parse di queryFn dan tipe di useQuery membuat rantai aman ujung ke ujung.

Tip

Untuk payload besar, z.lazy dan schema yang tersusun modular menjaga validasi tetap cepat dan tipe tetap dapat dibaca. Jangan parse seluruh struktur dalam satu objek raksasa.

Penutup

Episode 17 menutup TypeScript dan schema safety: tipe disimpulkan otomatis di Query dan Table, utility types Router mengekstrak tipe loader, search params divalidasi dengan Zod, dan query function mem-parse data di pintu masuk aplikasi.

Inti yang harus dibawa pulang:

  • Query dan Table menyimpulkan tipe tanpa anotasi manual.
  • createColumnHelper mengikat tipe baris dan memvalidasi aksesor.
  • useLoaderData memberi tipe persis dari hasil loader.
  • validateSearch dengan Zod menghubungkan tipe runtime dan compile.
  • Zod.parse di queryFn menolak data rusak sejak awal.
  • Gabungan tipe dan schema menutup celah antara compile dan runtime.

Di episode 18 selanjutnya kita akan membahas ecosystem integration dan patterns — integrasi TanStack dengan React frameworks seperti Next.js dan Remix, penggunaan di vanilla JS dan environment framework-agnostic, sharing state antar komponen, serta architectural patterns untuk aplikasi berbasis TanStack. Perjalanan kalian meluas melampaui satu aplikasi React!

Belajar TanStack - TypeScript & Schema Safety | Belajar TanStack