簡體   English   中英

為什么類型謂詞的否定會使對象成為 never 類型?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM