javascript - 為什麼 TypeScript 的 `never` 類型對條件物件需要問號?
這段程式碼可以正常運作,用來有條件地要求 isDirty 欄位成為 lecture 物件的一部分:
- 如果
id的類型是string,我必須添加一個isDirty欄位: - 如果
id的類型是number,我無法向該對象添加isDirty欄位。
type LectureT = LectureIdT & {
title: string;
};
/**
* Lecture must have an `isDirty` field if `id` is of type string, but should have no `isDirty` field if `id` if of type number.
*/
type LectureIdT =
| { id: string; isDirty: boolean }
| { id: number; isDirty?: never };
const lectureMockWithNumberId: LectureT = {
id: 1,
title: 'Lecture 1',
// isDirty: false, // Uncomment to see error
};
const lectureMockWithStringId: LectureT = {
id: "1",
title: 'Lecture 2',
isDirty: false,
};
這段程式碼可以在這個 TS Playground 中進行嘗試。
問題:為什麼我在 isDirty?: never 中需要問號?
我對此事的調查:
當我從 isDirty?: never 移除問號以得到 isDirty: never 時,它就不再工作了:
type LectureIdT =
| { id: string; isDirty: boolean }
| { id: number; isDirty: never };
lectureMockWithNumberId 突然出現 TypeScript 錯誤:
Type '{ id: number; title: string; }' is not assignable to type 'LectureT'.
Type '{ id: number; title: string; }' is not assignable to type '{ id: number; isDirty: never; } & { title: string; }'.
Property 'isDirty' is missing in type '{ id: number; title: string; }' but required in type '{ id: number; isDirty: never; }'.
我們在最後一句話中看到,它說 isDirty 是必需的。但我覺得這似乎不對!它的類型是 never -> 所以它應該 永遠 不會是必需的,不是嗎?
為什麼我必須加上問號才能使它工作。這似乎不合邏輯。
為了試圖理解它,我將其改為 isDirty: never | undefined:
type LectureIdT =
| { id: string; isDirty: boolean }
| { id: number; isDirty: never | undefined };
但令我驚訝的是,錯誤仍然存在!例如,a?: string 中的 ? 不就是等同於 a: string | undefined 嗎?
我不明白……
- ...為什麼這裡一開始就需要問號
?,而isDirty: never;基本上是不可能的。 - ...為什麼
?在與never一起使用時會有不同的表現?
答案#1
讓我一個一個地回答這些問題:
我們在最後一句看到,它說
isDirty是必填的。但這對我來說似乎不對!它的類型是never-> 所以它應該永遠不會是必填的,不是嗎?
never 類型在與可選物件屬性互動方面沒有任何特殊之處。你可以將 never 視為與其他類型(string、number 等)相同。不同之處在於,與這些其他類型不同,沒有任何值可以賦給 never。你可以說,never 類型的變數「永遠」無法被賦予有效值。
但令我驚訝的是,錯誤仍然存在!例如,
a?: string中的?不等於a: string | undefined嗎?
不。考慮這兩個物件之間的差異:{} 和 {a: undefined}。在一種情況下,鍵完全不存在。另一種情況下,它存在並且值為 undefined。前者物件可以賦值給 {a?: string},但不能賦值給 {a: string | undefined}。
這導致:
為什麼這裡一開始就需要問號
?,而isDirty: never;基本上是不可能的。
如同上述範例,如果你在鍵名稱上不加上 ?,那麼該鍵在類型中就是 必填的。這個鍵就是 必須存在的。但該鍵的值的類型是 never。因此,你無法為該鍵賦予任何有效的值。
所以簡而言之,確實沒有問號是不可能的,因為這是對需求的矛盾:那個鍵必須存在,但沒有值是有效的。
最後:
為什麼
?在與never一起使用時會有不同的表現。
這僅僅是 never 的通常屬性與 ? 的通常行為結合的結果。這兩個特定特性的相互作用沒有什麼特別之處。
以 {isDirty?: never} 為例,這表示以下情況之一必須為真:
- 該物件不包括
isDirty鍵。 - 該物件確實包含
isDirty鍵,並且它的值類型是never。
由於這兩個情況中的第二個是不可能的(根據上述論點),那麼只剩下第一個情況:符合這種類型的物件不得包含 isDirty 鍵。
答案#2
`never` 也被稱為空類型——在 `never` 類型的集合中沒有任何值存在。沒有值可以被賦給 `never` 類型的變數。即使你寫 `function f(): never`——函數的返回類型並不是 `never`——只是用來指定,該函數 `never` 返回(拋出異常或陷入無限循環)。
除了「函數不返回」之外,我不知道 never 的其他使用情況。