TypeScript 在面對遞迴行為的聯集判斷時,需要注意 TS 預設是每一次遞迴的結構都會檢查兩者。換言之,若你確定遞迴後的結果是非 A 即 B 的狀態,那會需要在聯集的兩個型別作準確指示。
內容
舉例,若我想要繼承現有的 react-router-dom 內的 IndexRouteObject 和 NonIndexRouteObject,並在期間加上兩個屬性 name, icon 以及僅 IndexRouteObject 有的 redirectTo
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import { IconType } from 'react-icons'; import { Navigate, IndexRouteObject, NonIndexRouteObject } from 'react-router-dom'; export interface CustomIndexRouteObj extends IndexRouteObject { redirectTo?: Navigate; } export interface CustomNonIndexRouteObj extends NonIndexRouteObject { name?: string; icon?: IconType; children?: (CustomIndexRouteObj | CustomNonIndexRouteObj)[]; } export type CustomRouteObj = CustomIndexRouteObj | CustomNonIndexRouteObj; |
那我在遞迴生成時,就會出現以下錯誤:
1 2 3 4 5 6 7 8 9 10 11 12 |
const transformRouteToLinkItem = (route: CustomRouteObj) => ({ name: route.name ?? '', path: route.path ?? '', icon: route.icon, children: route.children?.map(transformRouteToLinkItem), }); Property 'name' does not exist on type 'CustomRouteObj'. Property 'name' does not exist on type 'CustomIndexRouteObj'. Property 'icon' does not exist on type 'CustomRouteObj'. Property 'icon' does not exist on type 'CustomIndexRouteObj'.ts(2339) |
這樣的錯誤型式,就是暗示你 TypeScript 沒辦法區別你遞迴時傳進來的物件,到底是 A 或是 B。因此,你會需要將兩者都標上相同的 key,其中一個標為 never。這樣一來 TS 就可以知道一定是 A 或 B 其中一個。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import { IconType } from 'react-icons'; import { Navigate, IndexRouteObject, NonIndexRouteObject } from 'react-router-dom'; export interface CustomIndexRouteObj extends IndexRouteObject { name?: never; icon?: never; redirectTo?: Navigate; } export interface CustomNonIndexRouteObj extends NonIndexRouteObject { name?: string; icon?: IconType; children?: (CustomIndexRouteObj | CustomNonIndexRouteObj)[]; } export type CustomRouteObj = CustomIndexRouteObj | CustomNonIndexRouteObj; |
參考資料
1. Tagged Union Types in TypeScript: Leveraging Type Safety and Flexibility
按讚加入粉絲團