skills/typescript-frontend/SKILL.md
前端 TypeScript 規範:strict 模式、型別設計、泛型使用、型別窄化與 Vue 3 整合。當偵測到前端 TypeScript 專案時自動套用。
npx skillsauth add CloudyWing/ai-dotfiles typescript-frontendInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
當偵測到前端 TypeScript 專案(tsconfig.json 中包含 Vue/React 相關設定)或使用者要求撰寫前端 TypeScript 程式碼時,請自動套用以下規範。
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
}
}
strict: true 為強制項。新專案不允許關閉。| 選擇 | 適用情境 |
| --- | --- |
| interface | 物件形狀定義(API 回應、Props、State)、可擴展的契約 |
| type | 聯合型別、交叉型別、Mapped Types、Utility Types 組合 |
// ✅ interface:物件形狀
interface Order {
id: number;
customerName: string;
items: ReadonlyArray<OrderItem>;
note?: string;
}
// ✅ type:聯合型別
type OrderStatus = 'pending' | 'processing' | 'completed' | 'cancelled';
// ✅ type:複雜型別操作
type PartialOrder = Partial<Pick<Order, 'customerName' | 'note'>>;
// ❌ 禁止
function process(data: any) { }
const result: any = fetchData();
// ✅ 替代方案
function process(data: unknown) {
if (isOrder(data)) {
// 窄化後使用
}
}
// ✅ 泛型
function process<T>(data: T): T { }
any 完全繞過型別檢查,等同關閉 TypeScript。unknown,強制呼叫端做型別窄化。@types/* 套件;無法取得時,自行撰寫 .d.ts 宣告檔。// ❌ 避免:型別斷言(跳過檢查)
const order = response.data as Order;
// ✅ 正確:使用 Type Guard 做執行期驗證
function isOrder(data: unknown): data is Order {
return (
typeof data === 'object'
&& data !== null
&& 'id' in data
&& 'customerName' in data
);
}
if (isOrder(response.data)) {
// data 在此處為 Order 型別
}
as 不做執行期檢查,API 回傳格式變更時不會報錯。as 的情境:as const(常數斷言)、測試程式碼中已知結構的資料。// typeof guard
function formatValue(value: string | number): string {
if (typeof value === 'string') {
return value.toUpperCase();
}
return value.toFixed(2);
}
// in operator
function getArea(shape: Circle | Rectangle): number {
if ('radius' in shape) {
return Math.PI * shape.radius ** 2;
}
return shape.width * shape.height;
}
// Discriminated Union(推薦)
type ApiResult<T> =
| { success: true; data: T }
| { success: false; error: string };
function handleResult(result: ApiResult<Order>) {
if (result.success) {
// result.data 可用
console.log(result.data.id);
} else {
// result.error 可用
console.error(result.error);
}
}
// ❌ 避免 enum(產生額外的 JavaScript 程式碼)
enum Status {
Pending,
Active,
Closed
}
// ✅ 使用 const 物件 + as const
const Status = {
Pending: 'pending',
Active: 'active',
Closed: 'closed'
} as const;
type Status = typeof Status[keyof typeof Status];
// => 'pending' | 'active' | 'closed'
// ✅ 簡單場景直接用聯合型別
type Status = 'pending' | 'active' | 'closed';
enum 產生額外的 IIFE 程式碼,增加 bundle 大小。<script setup lang="ts" generic="T">
defineProps<{
items: ReadonlyArray<T>;
selected?: T;
}>();
const emit = defineEmits<{
select: [item: T];
}>();
</script>
// ✅ 泛型工具函式
function groupBy<T, K extends string | number>(
items: ReadonlyArray<T>,
keyFn: (item: T) => K
): Record<K, T[]> {
const result = {} as Record<K, T[]>;
for (const item of items) {
const key = keyFn(item);
(result[key] ??= []).push(item);
}
return result;
}
// 使用時自動推斷型別
const grouped = groupBy(orders, o => o.status);
T;多參數用有意義的名稱(如 TInput、TOutput)。| Type | 用途 |
| --- | --- |
| Partial<T> | 所有屬性可選 |
| Required<T> | 所有屬性必填 |
| Pick<T, K> | 選取部分屬性 |
| Omit<T, K> | 排除部分屬性 |
| Record<K, V> | 建立鍵值對型別 |
| Readonly<T> | 所有屬性唯讀 |
| ReturnType<T> | 取得函式回傳型別 |
| Parameters<T> | 取得函式參數型別 |
| Awaited<T> | 取得 Promise 解析後的型別 |
// 常見組合
type CreateOrderInput = Omit<Order, 'id' | 'createdAt'>;
type OrderUpdate = Partial<Pick<Order, 'customerName' | 'note'>>;
src/types/
├── order.ts # 訂單相關型別
├── customer.ts # 客戶相關型別
├── api.ts # API 通用型別(Response wrapper、Pagination)
└── index.ts # 統一匯出
export type(確保型別在編譯後被移除)。types.ts 中。// types/order.ts
export interface Order {
id: number;
customerName: string;
items: ReadonlyArray<OrderItem>;
}
export interface OrderItem {
productId: number;
quantity: number;
unitPrice: number;
}
export type OrderStatus = 'pending' | 'processing' | 'completed' | 'cancelled';
// ✅ API 回應型別
interface ApiResponse<T> {
data: T;
message: string;
}
interface PaginatedResponse<T> {
data: ReadonlyArray<T>;
total: number;
page: number;
pageSize: number;
}
// ✅ 非同步函式回傳型別明確標註
async function fetchOrder(id: number): Promise<Order> {
const response = await api.get<ApiResponse<Order>>(`/orders/${id}`);
return response.data.data;
}
import type { ComponentPublicInstance } from 'vue';
// Template Ref 型別
const formRef = ref<InstanceType<typeof MyForm> | null>(null);
// 使用
formRef.value?.validate();
// keys.ts
import type { InjectionKey } from 'vue';
import type { AuthService } from '@/services/auth';
export const authServiceKey: InjectionKey<AuthService> = Symbol('authService');
// 提供方
provide(authServiceKey, authService);
// 注入方
const authService = inject(authServiceKey);
if (!authService) {
throw new Error('AuthService 未提供');
}
// ✅ 原生事件型別
function handleClick(event: MouseEvent) { }
function handleInput(event: Event) {
const target = event.target as HTMLInputElement;
console.log(target.value);
}
tools
產生或補齊 .gitattributes,統一行尾處理、二進位識別與 lock files 標記,保留既有自訂偏好。
development
產生或補齊前端 Lint 設定(Prettier + ESLint Flat Config),統一格式化與程式碼品質規則,保留既有自訂偏好。
testing
依據事實校閱報告修改技術文件:以事實層為不可違反的約束,由改檔者負責表達層的措辭與行文連貫。Use when the user asks to apply fact-check results to a document, or to edit a document based on a previously produced fact-check-report.md.
data-ai
多份資料檔整合流程。當需要將兩份以上的資料檔(如 JSON、CSV)合併、補齊闕漏欄位或去重成單一檔案時使用。以 dry-run、筆數核對與抽樣比對降低整合錯誤。