Featured image of post 掌握 TypeScript 可以讓 Operator 滿意Featured image of post 掌握 TypeScript 可以讓 Operator 滿意

掌握 TypeScript 可以讓 Operator 滿意

了解 TypeScript 中的 satisfies 運算子如何根據自訂類型驗證物件結構而不遺失精確的文字類型推斷。

介紹

TypeScript 4.9 中引入的 satisfies 運算子是一項強大的功能,旨在提高類型安全性,同時保持開發人員的靈活性。

但是,區分 satisfies、類型註解 (: Type) 和類型斷言 (as Type) 之間的差異可能會令人困惑。本指南詳細介紹了 satisfies 運算子的機制,並分享了生產程式碼庫的實際用例。


1. 標準類型註解的限制(:)

讓我們回顧一下標準類型註釋的限制。

想像設計一個調色板主題配置。有些顏色被命名為字串文字,而其他顏色則為數字 RGB 陣列:

type Color = string | [number, number, number];

// Palette definition
type Palette = {
  primary: Color;
  secondary: Color;
  danger: Color;
};

// Declaring our palette using standard type annotations
const themePalette: Palette = {
  primary: "blue",
  secondary: [0, 128, 0], // Green (RGB)
  danger: "red"
};

一切編譯都很好。但是,如果我們存取 themePalette.primary 並嘗試運行像 toUpperCase() 這樣的字串方法,編譯器會抱怨:

// Error: Property 'toUpperCase' does not exist on type 'Color'.
// Property 'toUpperCase' does not exist on type '[number, number, number]'.
themePalette.primary.toUpperCase();

為什麼會出現這個錯誤?

透過使用 : Palette 註釋,我們指示 TypeScript 擴充 themePalette 的屬性以符合 Palette 類型定義。編譯器會丟棄 primary 被指派了字串 "blue" 的特定知識。相反,它假設 primary 在任何時候都可以是字串或數組,迫使您編寫繁瑣的類型保護。


2. 解決問題滿足

satisfies 運算子解決了這種情況。 它指示 TypeScript 驗證物件是否與特定類型結構匹配,但保留指定值的精確、最窄的推斷類型

重構滿足

const themePalette = {
  primary: "blue",
  secondary: [0, 128, 0],
  danger: "red"
} satisfies Palette; // Verify compatibility with Palette but keep the literal types

使用 satisfies 可以帶來兩個直接的好處:

好處 1:精確類型推斷保留

// Compiles perfectly!
// TypeScript knows themePalette.primary is definitely a string
themePalette.primary.toUpperCase();

// Also compiles perfectly!
// The compiler knows secondary is a 3-element tuple of numbers
themePalette.secondary.map(v => v * 2);

好處 2:及早發現拼字錯誤

由於編譯器會根據目標類型驗證物件結構,因此任何遺失的屬性或不正確的類型分配都會立即觸發編譯警告。

const badPalette = {
  primary: "blue",
  secondary: [0, 128, 0],
  danger: 12345, // Error: number is not assignable to type Color
  warning: "orange" // Error: warning does not exist on type Palette
} satisfies Palette;

3. 實際用例:保護記錄上的物件金鑰

另一個強大的用例是使用 Record 類型管理鍵值映射。

例如,定義使用者存取權限時:

type UserRole = "admin" | "editor" | "viewer";

// Map each role to a boolean access value
const rolePermissions = {
  admin: true,
  editor: true,
  viewer: false
} satisfies Record<UserRole, boolean>;

此處使用 satisfies 可確保:

  1. UserRole 中宣告的每個角色都必須在物件中定義。如果忘記包含 viewer,編譯器會拋出錯誤。
  2. rolePermissions 的鍵仍輸入為確切的角色字串 ("admin" | "editor" | "viewer"),而不是一般字串,從而啟用 IDE 自動完成功能。

結論:類型定義指南

編寫 TypeScript 時,請使用下列經驗規則來選擇正確的鍵入語法:

  1. 類型註解 (: Type): 將此用於函數參數、傳回簽名,或當您明確想要擴大類型聲明以限制對子屬性的存取時。
  2. 類型斷言 (as Type): 僅當解析外部 API 輸入時使用此選項,其中 TypeScript 無法解析類型,但您絕對確定結構(謹慎使用)。
  3. satisfies 運算子 (satisfies Type): 在聲明本機配置、常數或狀態記錄時使用它來驗證它們是否遵守定義的結構,而不丟棄所指派值的確切類型。

使用 satisfies 運算子可以幫助您編寫更乾淨、類型安全的程式碼,同時減少樣板轉換。