[英]TypeScript `extends` conditional type statement only works if expressed using Generics?
我試圖更好地理解 TypeScript 中的extends
關鍵字及其潛在應用。
我遇到的一件事是兩個內置實用程序,即Extract
和Exclude
,它們同時利用了extends
和條件類型。
/**
* Exclude from T those types that are assignable to U
*/
type Exclude<T, U> = T extends U ? never : T;
/**
* Extract from T those types that are assignable to U
*/
type Extract<T, U> = T extends U ? T : never;
我正在玩弄以更好地理解這種“縮小”是如何工作的,或者更好地說明“子集過濾”的工作原理,並嘗試創建我自己的實現只是為了查看它的實際效果,並且遇到了這種非常奇怪的行為:
type ValueSet = string | 'lol' | 0 | {a: 1} | number[] | 643;
type CustomExclude<T, U> = T extends U ? T : never;
// this works:
// type Result1 = 0 | 643
type Result1 = CustomExclude<ValueSet, number>;
// but this doesn't?
// type Result2 = never
type Result2 = ValueSet extends number ? ValueSet : never;
為什么會這樣?
我希望兩個實例都能返回正確的類型子集,但條件類型僅在通過泛型表達時才有效。
有人可以向我解釋這背后的邏輯嗎?
第二段代碼進行了一次檢查,以查看整個類型是否從 number 擴展。 如果是,則返回整個類型,否則返回never
。 帶有泛型的版本將遍歷聯合中的所有單個類型(第一個string
,然后是"lol"
,然后是0
等)並單獨評估它們。 然后,您將獲得幸存下來的任何個體類型的聯合。
可以從您的第二個示例中獲得一個非從不值,但前提是每個可能的值都是一個數字。 例如:
type Example = 1 | 3 | 5;
type Example2 = Example extends number ? Example : never;
// Example2 is 1 | 3 | 5
請參閱分配條件類型:
當條件類型作用於泛型類型時,當給定聯合類型時,它們會變成分布式的。 例如,采取以下措施:
type ToArray<Type> = Type extends any ? Type[] : never;
如果我們將聯合類型插入 ToArray,則條件類型將應用於該聯合的每個成員。
type ToArray<Type> = Type extends any ? Type[] : never;
type StrArrOrNumArr = ToArray<string | number>; // string[] | number[]
因此,如果您使用具有泛型類型的extends
,則整個條件類型適用於聯合中的每個元素。
如果您使用非泛型類型extends
,就像您在第二個示例中使用的那樣,條件類型適用於整個類型。
您甚至可以在第一個示例中關閉分配。 只需將您的泛型包裝在方括號中:
type ValueSet = string | 'lol' | 0 | {a: 1} | number[] | 643;
type CustomExclude<T, U> = [T] extends [U] ? T : never;
// never
type Result1 = CustomExclude<ValueSet, number>;
包裹在方括號中的泛型被視為非泛型類型,就像在您的第一個示例中一樣。
在實踐中,這種模式非常有用。 通常使用T extends any
來打開分配性。
假設您有一些對象類型。 您想獲取所有密鑰並對其應用一些修改器。 換句話說,映射它們。 考慮這個例子:
type Foo = {
name: string;
age: number
}
// non verbose approach, distributivity
type ChangeKey<T> = keyof T extends string ? `${keyof T}-updated` : never
type Result = ChangeKey<Foo>
// middle verbose approach
type ChangeKey1<T> = {
[Prop in keyof T]: Prop extends string ? `${Prop}-updated` : never
}[keyof T]
type Result1 = ChangeKey1<Foo>
// verbose approach
type ChangeKey2<T extends Record<string, unknown>> = keyof {
[Prop in keyof T as Prop extends string ? `${Prop}-updated` : never]: never
}
type Result2 = ChangeKey2<Foo>
您可能已經注意到, ChangeKey
比其他的更優雅。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.