![](/img/trans.png)
[英]Extending function parameter types with intersection type in Typescript
[英]typescript intersection types of function
我正在使用 typescript 並遇到了一些問題。 最簡單的演示是:
type g = 1 & 2 // never
type h = ((x: 1) => 0) & ((x: 2) => 0) // why h not never
type i = ((x: 1 & 2) => 0)// why x not never
我不明白為什么類型h
不是never
而類型i
中的 param x
不是never
type e = (((x: 1) => 0) & ((x: 2) => 0)) extends (x: infer L) => 0 ? L : never; // why e is 2 not never or 1?
另外,不明白為什么 type e
是2
而不是never
?
注意:非原始類型通常用 UpperCamelCase 編寫; 這將它們與原始類型和變量/屬性名稱區分開來。 我將在下面使用這個約定。
這個問題有很多部分,所以它的答案也會有很多部分。
讓我們先解決簡單的問題:
type G = 1 & 2 // never
在microsoft/TypeScript#31838中實現了像1 & 2
這樣的空交叉點的減少,並隨never
3.6 一起發布。 在此之前, 1 & 2
的處理方式與never
非常相似,因為您永遠無法找到滿足它的值。 1 & 2
和never
之間實際上沒有概念上的區別,盡管編譯器實現細節可能會導致一個與另一個被區別對待。
下一個:
type I = ((x: 1 & 2) => 0) // why x not never
x
是never
,但減少會延遲到您實際使用它:
type IParam = Parameters<I>[0]; // never
此延遲在microsoft/TypeScript#36696中實現,並與 TypeScript 3.9 一起發布。 在此之前, x
被急切地簡化為never
像上面的G
那樣。
現在來看一些更復雜的例子:
type H = ((x: 1) => 0) & ((x: 2) => 0) // why H not never
實際上有很多原因H
不是never
:
特定於 TypeScript 的原因是因為 function 類型的交集被認為與具有多個調用簽名的重載 function相同:
declare const h: H; // 1/2 h(x: 1): 0 // 2/2 h(x: 2): 0 h(1); // okay h(2); // okay h(3); // error, no overload matches this call h(Math.random() < 0.5? 1: 2); // error, no overload matches this call
注意h
如何顯示為具有兩個重載的 function; 一個接受1
參數,另一個接受2
參數。 它不接受3
,也不接受1 | 2
1 | 2
即使從純類型系統的角度來看它可能應該(參見microsoft/TypeScript#14107以獲得支持這一點的長期功能請求)。
即使 TypeScript 沒有將函數交集解釋為重載, H
也不應該是never
。 Function 類型的參數類型是逆變的(有關解釋,請參閱我對此問題的回答。您還可以在--strictFunctionTypes
編譯器標志的 TS 手冊描述中閱讀 function 參數的逆變性)。 逆變將事物變成它們的對偶; 如果F<T>
在T
中是逆變的,則F<T | U>
F<T | U>
等價於F<T> & T<U>
,而F<T & U>
等價於F<T> | F<U>
F<T> | F<U>
。 因此,一個一致的類型系統,當要求函數交集的參數類型時,將返回function參數類型的並集。 和1 | 2
1 | 2
不是never
。
即使H
是一個完全無人居住的類型,目前 TypeScript 只是在特定情況下將交叉點減少到never
,功能交叉點不是其中之一。 如果您查看編譯器源代碼文件checker.ts
,它會顯示“如果交叉類型包含類型never
或多個單元類型或 object 類型和可空類型( null
或undefined
),則認為它為空,或者類string
類型和已知非string
類型,或類number
類型和已知非類number
類型,或類symbol
類型和已知非symbol
類型 - void
void
沒有一個說“兩個不兼容的 function 類型”,所以它沒有減少到never
。
最后一個:
type E = (((x: 1) => 0) & ((x: 2) => 0)) extends (x: infer L) => 0 ? L : never;
// why E is 2 not never or 1?
回想一下,函數的交集被認為是重載的 function。 TypeScript 的一個已知設計限制是,在對重載的 function 類型使用類型推斷時,編譯器不會嘗試通過確定哪個調用簽名與預期的推斷最匹配來解決過載問題。 它只使用最后一個調用簽名並忽略其中的所有 rest 。 在E
中,這意味着編譯器僅在與(x: infer L) => 0
匹配時看到(x: 2) => 0
> 0 ,因此L
被推斷為2
。 “正確”的做法可能是聯合1 | 2
1 | 2
,但這不會發生。 有關此限制的一個實例,請參閱microsoft/TypeScript#27027 。
如果
type h = ((x: 1) => 0) & ((x: 2) => 0)
您將1
分配為x
的類型,也稱為文字類型。 文字類型幾乎never
最小的值集。 never
和字面量類型1
之間的區別在於, never
是一個空集,但1
集合中只有一個值。 所以never
!== 1
這里
type i = (x: 1 & 2) => 0;
參數x
的類型never
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.