Pinia ditulis dengan TypeScript dan menginferensikan type store secara otomatis. Episode ini membahas typing dasar dan generics di defineStore, menambah type properti plugin lewat PiniaCustomProperties, serta return type inference pada Setup store dan akses store lain yang fully typed.

Salah satu alasan Pinia dipilih tim besar adalah type safety penuh tanpa menulis type berulang. Tulis store kalian, dan TypeScript akan menginferensikan type state, getters, actions, bahkan this di dalam action — semuanya otomatis.
Episode 16 membahas TypeScript di Pinia: typing dasar dan generics di defineStore, menambah type properti plugin dengan PiniaCustomProperties, serta type inference pada Setup store dan akses store lain.
Kebanyakan store tidak butuh type eksplisit sama sekali:
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
},
})
// Penggunaan
store.count // number
store.double // number
store.increment() // void, this ter-typedthis.count++ di dalam action langsung ter-typed sebagai number tanpa anotasi apa pun. TypeScript membawa seluruh definisi store ke tempat pemakaiannya.
Tanpa inference, setiap perubahan nama field memaksa kalian mengupdate type di banyak tempat. Dengan inference, compiler mengikuti definisi store — satu sumber kebenaran. Ini mengurangi bug kelas "state diubah tapi UI tidak tahu tipe yang benar".
Saat state memakai struktur kompleks, definisikan interface lalu gunakan di state:
interface User {
id: number
name: string
}
export const useUserStore = defineStore('user', {
state: (): { current: User | null; list: User[] } => ({
current: null,
list: [],
}),
getters: {
activeUsers: (state) => state.list.filter((u) => u.id > 0),
},
})state: (): { current: User | null; list: User[] } => ... memberi Pinia tipe yang lengkap, sehingga getter activeUsers dan seluruh akses store.list ter-typed sebagai User.
Interface yang sama bisa dipakai di argumen action, sehingga tidak ada duplikasi:
actions: {
addUser(user: User) {
this.list.push(user)
},
}addUser(user: User) memakai interface User yang sama dengan state. Kalau struktur data berubah, TypeScript akan menunjuk semua tempat yang perlu diperbarui.
Properti yang ditambahkan plugin tidak dikenal TypeScript secara otomatis. Perluas modul Pinia lewat module augmentation:
declare module 'pinia' {
export interface PiniaCustomProperties<Id, S, G, A> {
$logger: (message: string) => void
}
}Dengan deklarasi di atas, setiap store akan memiliki $logger yang ter-typed:
pinia.use(({ store }) => {
store.$logger = (message) => console.log(`[${store.$id}]`, message)
})PiniaCustomProperties<Id, S, G, A> memperluas tipe instance store sehingga properti tambahan plugin ikut dikenali — IDE memberi autocomplete dan error saat salah memakai.
Setup store menginferensikan tipe dari nilai yang dikembalikan — tidak perlu anotasi tambahan:
export const useCartStore = defineStore('cart', () => {
const items = ref<{ name: string; price: number }[]>([])
function addItem(item: { name: string; price: number }) {
items.value.push(item)
}
return { items, addItem }
})
store.addItem({ name: 'Kopi', price: 15000 })
store.items // Ref<{ name: string; price: number }[]>defineStore('cart', () => ...) menyimpulkan bahwa items adalah Ref dan addItem adalah fungsi.
Mengakses store lain dari store lain juga ikut ter-typed otomatis:
const userStore = useUserStore()
userStore.name // string, dikenal compiler
userStore.setName('Arman') // tipe argumen divalidasiuseUserStore() mengembalikan instance store yang sudah memiliki tipe lengkap. Menyerahkan argumen dengan tipe salah akan langsung ditolak compiler sebelum masuk runtime.
Tip
Untuk tipe this di action yang diperluas plugin, gunakan PiniaCustomStateProperties — analog dengan PiniaCustomProperties tetapi untuk state dan this.
Episode 16 menunjukkan betapa dalamnya integrasi Pinia dengan TypeScript. Kalian sekarang bisa menikmati inference otomatis, memakai interface untuk state kompleks, mengetik properti plugin dengan PiniaCustomProperties, dan memahami return type inference pada Setup store.
Inti yang harus dibawa pulang:
this di action.PiniaCustomProperties mengetik properti tambahan dari plugin.Di episode 17 selanjutnya kita akan membahas testing dengan @pinia/testing — createTestingPinia untuk membuat pinia tiruan, reset state antar test, mocking action dengan vi.fn, serta component testing dengan Vue Test Utils dan Vitest.