假設我有一個受約束的型別函式type Foo<T extends string>,它只接受擴展的型別string。我希望以下代碼能夠編譯。相反,它給出了一個型別錯誤。
type Foo<T extends string> = T;
type UseFoo1<T> = NonNullable<T> extends string
? (param: Foo<NonNullable<T>>) => void
^^^^^^^^^^^^^^
: () => void;
Type 'NonNullable<T>' does not satisfy the constraint 'string'.
Type 'T' is not assignable to type 'string'.
為什么 TypeScript 不能派生NonNullable<T>擴展string?
在以下情況下,它確實有效。
type UseFoo2<T> = T extends string
? (param: Foo<T>) => void
: () => void;
type UseFoo3<T> = NonNullable<T> extends string
? Foo<NonNullable<T>>
: never;
uj5u.com熱心網友回復:
通常,在通過跟蹤條件型別檢查邏輯暗示的每個型別事實獲得的有用性與跳過它們獲得的有用性之間存在權衡。跟蹤它們可為您提供更多資訊,跳過它們可為您提供更好的編譯器性能。而且跟蹤也很難正確進行。當你有時,面對方差,將運算式的新約束一直傳播到型別運算式中WWW extends XXX ? YYY : ZZZ并不總是正確的;如果出現在協變位置,則可以傳播約束,但如果它出現在逆變位置XXXWWWYYYWWWYYY位置,那么這樣做并不總是可取的。所以我想說,無論 TS 團隊如何決定實施這種約束傳播,都會有未滿足的用例。
特別是,您似乎看到了microsoft/TypeScript#43427錯誤修復的副作用,其中以下代碼給出了錯誤:
type Q<T> = number extends T ? (n: number) => void : never;
function fn<T>(arg: Q<T>) {
arg(10); // error in TS 4.2-, okay in TS 4.3
}
編譯器縮小number到交叉點 number & T,然后抱怨10顯然不能分配給number & T。因為number出現在 中的一個逆變位置(n: number) => void,這種縮小使得number函式型別更寬,用處更少。
在microsoft/TypeScript#43439中實作了一個修復,以不將約束傳播到逆變位置,在microsoft/TypeScript#43599T中實作了另一個修復,以便在檢查的型別是型別引數(like和 distinct numberor )時專門重新啟用此傳播NonNullable<T>。這修復了 TypeScript 4.3 及更高版本中的上述錯誤,但引入了您遇到的錯誤。
這就是它發生的原因。由于型別引數不受此限制,您可以通過使用條件型別推斷將您的型別復制到新的型別引數中來解決它,并且在 TS4.7 及更高版本中,您甚至可以重新約束檢查的型別,以便重構不會t 實際上改變了你的代碼:
type UseFoo2<T> = NonNullable<T> extends infer NNT extends string ?
(param: Foo<NNT>) => void : () => void; // okay
這會復制NonNullable<T>到型別引數中,但如果被限制為NNT,我們只會進入真正的分支。并且您可以在任何需要將型別限制為.NNTstringNNTstring
Playground 代碼鏈接
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/495743.html
上一篇:創建型別的通用變體的列舉
下一篇:c#中如何統一同類方法
