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.

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.
TanStack Query menyimpulkan tipe data dari nilai yang dikembalikan queryFn. TanStack Table menyimpulkan tipe baris dari generic yang diberikan ke createColumnHelper:
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.
Tipe hasil query dan loader tersimpan di level tipe. Utility types TanStack Router memungkinkan mengekstraknya:
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.
Router menyimpulkan tipe search dari validateSearch. Dengan Zod sebagai validator, tipe runtime dan tipe compile-time menjadi satu sumber:
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.
Query function sering dianggap berbahaya: data dari server bisa berbentuk apa pun, apalagi di JSON. Parse di pintu masuk aplikasi menutup celah ini:
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.
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:
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!