
TypeScript - satisfies
想必大家在開發 TypeScript 的過程中,多少都會遇到一個情況:如果有一個 interface 其中之一的屬性是聯集型別,例如 string | number。如果將該 interface 指定給一個物件,且要使用到該屬性時,就會發現只能使用 string 和 number 都共有的屬性。
satisfies 來解決型別推導不精確的問題。但既然有了斷言和 Type Guard,又為何需要 satisfies 來解決問題呢?接下來,就讓我們來看看 satisfies 究竟解決什麼問題,又該如何使用吧!
什麼時候需要用到 satisfies?
satisfies 是為了解決在聯合型別或擴展型別的情況下,在不修改變數所屬型別的情況下,靜態推導型別的問題。satisfies 的使用場景,主要集中在以下兩個方面:
- 在不修改原有型別的情況下,確保資料符合特定型別。
- 進行更細部的靜態型別推導,同時避免引入執行時的負擔。
satisfies 的使用方式
satisfies 的使用方式,其實主要的目的和 interface 與 type 的效果接近。
interface Obj1 {
a: number;
b: string;
}
const obj1 = {
a: 1,
b: 2, // Type "number" is not assignable to type "string".
} satisfies Obj1;
接下來,就讓我們透過實際案例,來進一步了解 satisfies 吧!
一、為聯集屬性推定型別
假設有兩種 Task 的形式,一種是 DetailedTask,另一種則是 SimpleTask。當我們生成一個新的 User 物件時,task 的屬性有可能是一個物件或 string。但 TypeScript 沒辦法確認資料有可能是物件還是 string,因此在使用 string 專屬的 charAt 方法時就會報錯。
type SimpleTask = string;
interface DetailedTask {
description: string;
dueDate: Date;
}
type Task = SimpleTask | DetailedTask;
interface User {
name: string;
task: Task;
}
const badUser: User = {
name: "John",
task: "Finish report"
}
badUser.task.charAt() // charAt 會報錯
方案一:透過斷言 as 指定型別
直接透過 as 斷言來確認 badUser 的型別,但帶來的缺點也顯而易見。除了程式碼的易讀性會變低,也會讓程式碼脫離 TypeScript 的型別檢查。因為 as 的目的就是告訴編譯器,不要管我在做什麼,我告訴你這是什麼。
(badUser.task as SimpleTask).charAt(0); // OK
方案二:透過型別謂詞縮減型別
透過型別謂詞來確定型別。透過判別是否為 SimpleTask,讓 TS 知道如何辨別所屬的物件,進而提供可選用的方法。若想進一步了解型別謂詞,可參考 10 分鐘學會 TS 中必會的 5 種型別防禦 Type Guard。
function isSimpleTask(task: Task): task is SimpleTask {
return typeof task === "string";
}
if (isSimpleTask(badUser.task)) {
badUser.task.charAt(0); // ok
}
方案三:透過 satisfies 自動推導型別
最後,便是使用 satisfies 來自動推導型別。相較於上面兩個方法,satisfies 能夠自動推導出 badUser.task 屬於 string 型別,因此能夠自動提供 charAt() 的方法。
const goodUser = {
name: "Cat",
task: "Finish the report",
} satisfies User;
goodUser.task.charAt(); // ok
與此同時,當我們滑到 goodUser.task 上時,可以看到編譯器已經幫我們推導出 string 的型別,讓我們能夠自然而然的使用 string 的方法 trim。

satisfies type inference
二、動態推定擴展型別的資料型別
除了聯集型別可以推定,也可以推定擴展型別,如 [key: string]: any;TypeScript 僅會提示顯式聲明型別的屬性,但屬於擴展型別範圍的則不會顯示。例如顯式指定了 a、b 兩個屬性。
type Obj = {
a: number,
b: string,
[key: string]: any,
}
const objType: Obj = {
a: 1,
b: 2, // 顯示型別錯誤
c: 3, // 不會自動推導
}

因為 c 並沒有在 Obj1 中被指定,則在選則 c 屬性時,編譯器並不會有任何提示。可以發現下面的自動補齊完全沒有出現 c。
使用 satisfies 後的差異
相反地,使用 satisfies 後,則可以在顯示聲明時檢查顯式聲明 a、b,而自動推導 c 的型別。
type Obj = {
a: number,
b: string,
[key: string]: any,
}
const ObjSatisfies = {
a: 1,
b: 2, // 顯示型別錯誤
c: 3, // 自動推導為 number
}

可以發現右邊的 obj1. 出現了 c 的自動補齊,代表編譯器有自動推導出 c 的屬性與型別,且 c 的屬性也被正確推導為 number,因而能夠使用 number 特有的屬性。
因此善用 satisfies,可以讓我們在動態推導型別時,能夠兼具程式碼的簡潔,同時也能維護程式碼的型別安全,並讓自動補齊能夠順利做動。
結論與延伸
透過這些例子,我們其實可以發現 TypeScript 其實擁有多種方式可以提升型別安全,但在個別的情境下,又有各自合適的案例。因此當我們需要為一個 type 或 interface 檢驗其屬性的型別,屬於聯集型別的哪一種時,就可以使用 satisfies 來達成目的。
如果有任何的筆誤或想法,都歡迎你留言一起討論,讓我們一起深入探索 TypeScript 的強大功能吧!



















