[英]Why does the negation of a type predicate make an object be of type never?
我想創建一種方法來指定特定對象的屬性(可以為 null)是否為 null。 我想這樣做,以便我可以過濾掉所有屬性確實為 null 的對象,以便我可以安全地使用該對象並訪問其屬性。
在此示例中, Container
是對象, nullableContent
是屬性。 我想通過在Container
的泛型參數中使用條件類型來指定該屬性不為空。 如果HasThing
為 true,則nullableContent
不為 null。 如果為假,則為空。
這似乎在大多數情況下都有效,即使在創建類型謂詞hasContent
。 在hasContent(container) === true
的情況下,它可以計算出container
的類型為Container<true>
。 但是,在hasContent(container) === false
的情況下,TypeScript 認為container
的類型為 never,因此我無法再訪問其任何屬性。
為什么會發生這種情況? 為什么 TS 不能算出container
現在只是Container<false>
類型?
interface Container<HasThing extends boolean = boolean> {
id: string;
nullableContent: HasThing extends true ? string : null;
}
const hasContent = (container: Container): container is Container<true> =>
!!container.nullableContent;
function doThing(container: Container) {
if (hasContent(container)) {
return container;
} else {
// Below line gives error "Property 'id' does not exist on type 'never'.ts(2339)"
throw new Error(`Container with ID ${container.id} is empty`);
}
}
編輯:我在 TypeScript playground創建了這個例子。
縮小范圍對工會最有效。 使用false
分支上的聯合,編譯器可以從聯合中取出在true
分支上處理的成分。
如果沒有要縮小的聯合,編譯器會將原始類型與守衛類型相交,這將在true
分支上計算為Container<true>
,但在 false 分支上,它將嘗試執行類似Exclude<Container, Container<true>>
導致never
在這里(仍然試圖圍繞為什么......)
最簡單的解決方案是將接口轉換為聯合:
type Container = {
id: string;
} & ({ nullableContent: string } | { nullableContent: null })
const hasContent = (container: Container): container is Exclude<Container, { nullableContent: null }> =>
!!container.nullableContent;
function doThing(container: Container) {
if (hasContent(container)) {
return container;
} else {
// Below line gives error "Property 'id' does not exist on type 'never'.ts(2339)"
throw new Error(`Container with ID ${container.id} is empty`);
}
}
或者,如果您想保留類型參數:
type Container<HasThing extends boolean = boolean> = {
id: string;
} & (HasThing extends true ? { nullableContent: string } : { nullableContent: null })
const hasContent = (container: Container): container is Container<true> =>
!!container.nullableContent;
function doThing(container: Container) {
if (hasContent(container)) {
return container;
} else {
// Below line gives error "Property 'id' does not exist on type 'never'.ts(2339)"
throw new Error(`Container with ID ${container.id} is empty`);
}
}
看起來 TS 無法縮小類型。 地獄我不知道為什么,但我可以提出至少會改變/影響你的代碼的解決方案。 將類型定義更改為條件聯合,如:
type Container<T extends boolean = boolean> = T extends true ? {
id: string;
nullableContent: string;
} : {
id: string;
nullableContent: null;
}
現在您的其余代碼可以使用適當的類型縮小和推理。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.