Skip to content

型別繼承

物件型別可以繼承一個或多個父物件型別。子型別會繼承每個父型別的每個欄位,並可以用同名重新定義覆寫掉任何繼承的欄位。這在兩個型別有大部分共同 shape、只有少數欄位不同的情況下最對味——不用再把 idcreatedAtupdatedAt 在十幾個相似型別裡重複寫一遍。

繼承是可選的:沒有 extends 的型別跟以前一模一樣。

宣告 extends

在型別建構器選一個物件型別,欄位清單上方會出現新的 Extends 標籤選擇器。從下拉式選單挑一個父型別,它會變成一個標籤;再挑一個就多一個父型別;點標籤上的 × 移除。

候選父型別已經幫你篩過了:

  • 只有其他物件型別會出現(primitive、union、array 不能被繼承)。
  • 當前選中的型別不能選自己。
  • 任何會形成循環(A extends B extends A)的父型別會自動排除。

父型別如果之後被刪除或改成非物件 kind,會在型別建構器上方的警告列裡顯示為 broken ref——跟 $ref 斷掉的處理一樣。

父型別標籤可以拖拉重排以調整優先順序:抓起一個標籤拖到另一個上,或用鍵盤拖拉(Space 抓取、方向鍵移動、Space 放下、Escape 取消)。

繼承欄位面板

一個型別至少有一個父型別後,標籤列下方會多一個繼承欄位面板,一行一個,從整條父鏈聚合後以淡灰呈現。每行顯示欄位名、kind 徽章、是否必填,右側有一個 Override 按鈕。

Override 會把這個繼承欄位複製到子型別的 fields[] 裡當作一個新行,預先填好父型別的定義。繼承面板裡的那行會消失(因為子型別現在自己擁有了),而子型別裡的那行會多一個 (override) 徽章和一個小小的 Revert)按鈕。

  • 覆寫過後的欄位可以像其他欄位一樣編輯——改型別、翻轉必填、調整 constraints。子型別的定義會贏。
  • Revert 會移除覆寫、還原成父型別的定義。這行會回到繼承面板裡。

覆寫語意

  • 子型別完勝:名字跟父型別相同的欄位,覆寫是完整重新定義,沒有部分合併。
  • 優先順序:父型別按照你挑選的順序由左到右合併,後面的父型別蓋掉前面的,子型別蓋掉全部。所以 Foo extends [Bar, Baz] 加上 Bar: {x, y}Baz: {y, z}、child Foo: {z, w} 的結果是:
x  — 來自 Bar
y  — 來自 Baz(覆寫 Bar)
z  — 來自 Foo(覆寫 Baz)
w  — 來自 Foo(新增)
  • strict 模式是 OR 聚合的。 只要整條鏈上有任何一個標 strict,最終型別就是 strict。子型別可以自己標 strict,但無法把父型別的 strict 取消掉。
  • 不能移除繼承的欄位。OpenAPI allOf 沒有「減掉某欄位」的語法。如果想要沒有某個欄位的父型別版本,就不要繼承它。
  • 沒有 generic / 條件繼承。 只有單純的結構繼承。

消費者看到什麼

任何需要有效 shape(相對於宣告層級)的地方,應用會即時攤平:

  • Runtime 驗證會檢查繼承下來的必填欄位。缺少父型別 id 的 response 會被拒絕。
  • Run panel 的範例生成會走遍整條鏈,所以繼承的欄位也會出現在初始 payload 裡。
  • 規格差異比較的是解析後的 shape,所以把平鋪型別重構為 Foo extends Base 且最終 shape 不變會得到空的 diff。

型別建構器本身攤平——它一直展示你宣告的層級,讓你自然地繼續編輯。只有繼承欄位面板會透過 resolver 算出結果。

OpenAPI 來回保真

繼承直接對應到 allOf

yaml
User:
  allOf:
    - $ref: '#/components/schemas/Base'
    - type: object
      properties:
        name: { type: string }
      required: [name]
  • 匯出——父型別變成 $ref;子型別自己的欄位(如果有的話)會落在一個 inline object 成員裡。只繼承不加欄位的型別會匯出成 allOf: [...refs],沒有 inline 成員。
  • 匯入——allOf 裡有一個或多個 $ref 加上最多一個 inline object 會被還原成 { extends: [...refs], fields: inline.fields }。allOf 裡只有多個 inline object、沒有 $ref 的情況會走舊的「攤平合併」邏輯,這樣這個功能出現之前的歷史規格仍然可以讀。
  • 有資料夾的父型別會用攤平鍵的形式匯出(鍵為 auth/Base 的父型別會變成 auth_Base),並用 x-folder extension 在匯入時還原路徑。

JSON Schema 與 Markdown

  • JSON Schema 匯出使用相同的 allOf 模式,用 #/$defs/… 來參考。
  • Markdown 匯出會在每個有繼承的型別 JSON skeleton 之前加一行 Extends:,每個父型別都是跳到該段落的可點擊連結。param tables 裡的 ref 現在也是可點擊的錨點。

從平鋪型別遷移

如果你已經有一個複製了另一個型別欄位的平鋪型別,遷移是一個小編輯:

  1. 保留父型別不動。
  2. 在型別建構器開啟子型別,在 Extends 選擇器加上父型別。
  3. 凡是已經跟父型別定義一樣的欄位,從子型別裡刪除(繼承那一行會自動回來)。
  4. 凡是不一樣的欄位留下——它們就是 overrides。

跟遷移前的版本跑一次規格 diff 來確認語意沒變。空的 diff 代表重構是安全的。

繼承做的事

  • 不支援非物件的 extends。 只限物件型別。
  • 不能移除繼承的欄位。 OpenAPI 沒辦法表達減法。
  • 沒有 generic / 條件繼承。 只有單純的結構繼承;沒有型別參數。
  • 沒有「最終 shape」預覽面板(延後的 polish)。繼承面板與 override 列已經足以看到全貌。