簡體   English   中英

TypeScript `extends` 條件類型語句僅在使用泛型表達時才有效?

[英]TypeScript `extends` conditional type statement only works if expressed using Generics?

我試圖更好地理解 TypeScript 中的extends關鍵字及其潛在應用。

我遇到的一件事是兩個內置實用程序,即ExtractExclude ,它們同時利用了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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM