定義好規格的型別說明書,在開發階段針對型別幫我抓錯,編譯成純 JavaScript,執行在任何瀏覽器上。(但編譯後無法抓出錯誤型別XD)
/src/api/services/activity.ts 建立 api router
import { CreateActivityFilter, ActivityListResponse } from '../types/activity';
export const getActivityList = (data: **CreateActivityFilter**) =>
// <ActivityListResponse> 確保了 res.data 的型別
http.post<**ActivityListResponse**>(`/activity/list`, data);
params 和 response types 都得要在 /src/api/types/activity.ts 建立輸入和輸出的型別,確保資料長得正確
// api/services 中要傳送的 payload
export type **CreateActivityFilter** = {
category?: string[];
keyword: string;
page: number;
}
// 單一活動的詳細內容格式
export type **ActivityItem** = {
id: string;
title: string;
date: string;
isActive: boolean;
}
// api/services 中該 function 得到的 response
export type **ActivityListResponse** = {
activities: **ActivityItem**[]; // 裡面裝著活動項目的陣列
totalCount: number;
}
/src/stores/activity.ts 將 API 抓回來的資料交給 Pinia 管理,並自動補全型態
import { defineStore } from 'pinia';
import { getActivityList } from '@/api/services/activity';
import { ActivityItem, CreateActivityFilter } from '@/api/types/activity';
export const useActivityStore = defineStore('activity', {
state: () => ({
// 使用 "as" 斷言,告訴 TS 這是一個 ActivityItem 型別的陣列
list: [] as ActivityItem[],
loading: false
}),
actions: {
// 這裡可以接收參數,並傳給 API service
async fetchActivityList(filter: CreateActivityFilter) {
this.loading = true;
try {
const res = await getActivityList(filter);
// 因為 API service 已經定義了 Response 型別
// 這裡的 res.activities 會被 TS 自動識別為 ActivityItem[]
this.list = res.activities; // this 可以直接拿到資料,因為都在同一個物件實例上
return res;
} catch (err) {
return err;
}
}
}
});
在檔案下呼叫該 api
// 利用 store 儲存的 state 取資料
const activityStore = useActivityStore();
try {
// 呼叫時,TS 會強迫你傳入符合 CreateActivityFilter 格式的物件
await activityStore.fetchActivityList({
keyword: '',
page: 1,
category: ['運動', '講座']
});
} catch (error) {
// 處理錯誤邏輯
}
以上整理完畢,從Vuex Store 進化到 Pinia 搭配 TypeScript 的流程,希望之後別再忘記了!