網站搜尋

Visual Studio Code - 我如何查看 TypeScript 類型的完整展開契約?


如果我有一個看起來有點像這樣的類型集合:

type ValidValues = string | number | null
type ValidTypes = "text" | "time" | "unknown"

type Decorated = {
  name?: string | null
  type?: ValidTypes
  value?: ValidValues
  title: string
  start: number
}

type Injected = {
  extras: object
}

// overriding the types from Decorated
type Text = Decorated & Injected & {
  name: string
  type: "text"
  value: string
}

我的實際程式碼有更多的內容,但這展示了核心概念。我不想必須信任自己能夠正確處理類型之間的關係。我希望工具能在經過所有類型運算後,顯示出 Text 的類型定義“評估”結果。

所以對於上述範例,我希望在 Text 中指定的欄位能覆蓋在 Decorated 類型中先前聲明的內容,而假設的提示工具的輸出應該會顯示像這樣的內容:

{
  name: string
  type: "text"
  value: string
  title: string
  start: number
  extras: object
}

有沒有方便的方法可以獲取這些資訊?

答案#1

使用 IntelliSense 顯示類型的快速資訊通常不盡如人意;對於任何給定的類型,你通常只能得到單一的表示方式,而這可能對你的用途來說過於簡略甚至過於冗長。有一些建議可以使其更靈活(例如,microsoft/TypeScript#25784 和 microsoft/TypeScript#28508),以便使用者能在 IDE 中展開或摺疊類型定義。但我不知道這些建議是否會在近期甚至遠期內被採納,所以我們不要等著它們發生。


這裡有一個我有時候會使用的類型別名,用來嘗試以你所說的方式擴展一個類型:

// expands object types one level deep
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;

// expands object types recursively
type ExpandRecursively<T> = T extends object
  ? T extends infer O ? { [K in keyof O]: ExpandRecursively<O[K]> } : never
  : T;

這些使用條件型別推斷將型別 T「複製」到新的型別變數 O,然後使用類似同一性的映射型別遍歷複製型別的屬性。

條件型別推斷在概念上是一個無操作(no-op),但它被用來分配聯合型別並強制編譯器評估條件的“真”分支(如果你重新定義 Expand<T> 而不使用它,有時編譯器會直接 輸出 映射型別 {[K in keyof RelevantType]: RelevantType[K]},這不是你想要看到的)。

Expand 和 ExpandRecursively 的區別在於它是否僅輸出原樣的屬性類型(Expand),或者是否應該 展開 屬性類型(ExpandRecursively)。在遞歸的情況下,不嘗試深入基本類型會比較有幫助,這就是為什麼會包含 T extends object 條件的原因。


好的,讓我們看看當我們將它用在你的類型上會發生什麼。我們在你的情況下不需要 ExpandRecursively,但我們可以使用它……結果是一樣的:

type ExpandedText = Expand<Text>;

當我們在 IDE 中將滑鼠懸停在它上面時(無論是在 TypeScript Playground 還是 VSCode 中),它會顯示為:

/* type ExpandedText = {
  name: string;
  type: "text";
  value: string;
  title: string;
  start: number;
  extras: object;
 } */

如你所願。

程式碼連結

答案#2

只是補充 jcalz 的回答,提供能用於函數的版本。

修改過的助手:

export type Expand<T> = T extends (...args: infer A) => infer R
  ? (...args: Expand<A>) => Expand<R>
  : T extends infer O
  ? { [K in keyof O]: O[K] }
  : never;

export type ExpandRecursively<T> = T extends (...args: infer A) => infer R
  ? (...args: ExpandRecursively<A>) => ExpandRecursively<R>
  : T extends object
  ? T extends infer O
    ? { [K in keyof O]: ExpandRecursively<O[K]> }
    : never
  : T;

為了測試,我正在向 Text 類型添加一個 subobject 鍵,並添加一個函數接口:

type Text = Decorated &
  Injected & {
    name: string;
    type: "text";
    value: string;
    subobject: Injected;
  };

interface SomeFunction {
  (...args: Text[]): Injected & { error: boolean };
}

這給出:

物件仍然運作正常:

答案#3

我發現這個 VS Code 擴展在這方面運作得非常完美:

美化 TypeScript:更好的類型預覽

它在懸停時甚至顯示深層嵌套的類型。