TypeScript類型推斷的迷惑行為:四種函數返回值類型定義為何結果相同,以及聯合類型在條件類型中的陷阱?

typescript 類型推斷的奇異行為:四種函數返回值類型定義的相同結果及條件類型中聯合類型的陷阱

本文分析了 typescript 中一些令人費解的類型推斷行為。我們將探討四種看似不同的類型定義為何得出相同的結果,并解釋在條件類型中使用聯合類型時可能遇到的問題。

首先,讓我們觀察這四種 getReturnType 類型定義:

type getReturnType1<T> = T extends (...args: never) => infer R ? R : never; type getReturnType2<T> = T extends (...args: never[]) => infer R ? R : never; type getReturnType3<T> = T extends (...args: any[]) => infer R ? R : never; type getReturnType4<T> = T extends (...args: any) => infer R ? R : never;

盡管 …args 的類型分別為 never、never[]、any[] 和 any,這四種定義在實際應用中卻產生相同的結果。這是因為 TypeScript 的類型系統中,函數參數類型在類型推斷中的作用并非我們直覺所想的那樣。never 代表永不存在的值,never[] 代表空數組,any[] 代表任意類型數組,any 代表任意類型。然而,extends 條件判斷主要關注的是函數的返回值類型 infer R,參數類型的影響相對較小,甚至可以忽略。因此,只要傳入的 T 是函數類型,都能推斷出返回值類型并賦值給 R;若 T 不是函數類型,則返回 never。

接下來,我們分析另一個問題,涉及條件類型和聯合類型:

type Props<T extends Major | ResCategoryLabel> = {   labels: T[];   setSelect: (index: number, label: T extends Major ? Major : ResCategoryLabel) => void;   xxx; };  const changeSelect = (   index: number,   label: Major | ResCategoryLabel,   e: React.MouseEvent<HTMLAnchorElement> | React.TouchEvent<HTMLAnchorElement> ) => {   setSelect(index, label); // 類型錯誤   activeTabToCenter(e.currentTarget as HTMLElement); };

這段代碼中,Props 類型定義了一個泛型 T,限制其為 Major 或 ResCategoryLabel 的聯合類型。setSelect 函數的第二個參數 label 的類型定義為 T extends Major ? Major : ResCategoryLabel。本意是想根據 T 的類型確定 label 的類型:若 T 為 Major,則 label 為 Major;否則為 ResCategoryLabel。然而,由于 T 是聯合類型,TypeScript 無法在編譯時確定 T 的具體類型,導致 setSelect 函數的類型檢查失敗。這是因為條件類型處理聯合類型時,會分別對每個類型進行判斷,最終結果是所有分支結果的聯合。在這種情況下,label 的類型最終會變成 Major | ResCategoryLabel,與 changeSelect 函數中 label 的類型不匹配,從而導致類型錯誤。解決方法可能需要重新設計類型定義,例如使用類型守衛或更精細的類型推斷來避免此問題。

TypeScript類型推斷的迷惑行為:四種函數返回值類型定義為何結果相同,以及聯合類型在條件類型中的陷阱?

? 版權聲明
THE END
喜歡就支持一下吧
點贊15 分享