[英]Typescript function object parameter changes return type
我一直在努力嘗試使 object 參數影響 function 的返回類型。
這是一個帶有createArray
function 的示例,向您展示我的意思。
interface Selectable { selectable: boolean; }
interface SelectableReturn { selected: {value:any, index:number} }
interface Distant { distant: boolean; }
interface DistantReturn { distances: number[]; }
// All possible type combinations (all objects we can pass to the function)
type Possibilties = Distant | Selectable;
// Here we create the function definitions
declare function createArray(arr: any[], options?: undefined): any[];
declare function createArray(arr: any[], options?: Selectable): any[] & SelectableReturn;
declare function createArray(arr: any[], options?: Distant): any[] & DistantReturn;
declare function createArray(arr: any[], options?: Selectable & Distant): any[] & SelectableReturn & DistantReturn;
在未來,您可以想象為 object 添加新選項。 你能看出你會花多少時間來實現這個嗎? 如果我有 5 個選項,我必須有 5² = 25 function 聲明......
我想要的是根據選項類型自動將 append 屬性添加到返回的數組中。
如您所見,此代碼有效,但是當您像這樣創建數組時:
const arr = createArray([1,2,3], {selectable: true});
你沒有得到智能感知,這是因為“可能性”接口......有沒有辦法檢測“選項”類型,推斷返回類型並維護智能感知?
這是 Typescript 游樂場的鏈接。
我在這里采用的方法是創建一個通用調用簽名,它使用options
輸入參數的類型來生成所需形狀的 output 類型,這將是與options
中每個鍵對應的結果類型的交集范圍。
這里有幫助的一件事是定義一個類型CreateArrayOptions
表示輸入選項鍵到 output 返回類型的映射,以便我們可以以編程方式將結果類型確定為選項鍵的 function:
interface CreateArrayOptions {
selectable: SelectableReturn;
distant: DistantReturn;
}
而不是使用Selectable
和Distant
類型,我們將只使用 map 的CreateArrayOptions
到boolean
屬性的鍵......好吧,實際上只是true
,因為我不想擔心甚至不支持有人將選項設置為false
。 理想情況下,您只是不包含該選項,而是將其設置為false
...如果您需要支持false
,這是可能的,但更復雜。
這意味着createArray()
調用簽名將如下所示:
declare function createArray<K extends keyof CreateArrayOptions = never>(
arr: any[], options?: Record<K, true>
): any[] & Return<K>;
其中泛型類型參數K
是options
鍵的聯合(如果未傳入options
,則never
不會)。 而Return<K>
應該是對應的*Return
類型的交集。
以下是計算方法:
type Return<K extends keyof CreateArrayOptions> =
{ [P in K]: (x: CreateArrayOptions[P]) => void }[K] extends
(x: infer I) => void ? I : never
這會產生交叉點可能並不明顯。 如果你想要一個union ,你可以像CreateArrayOptions[K]
一樣簡單地做到這一點,因為索引到一個帶有聯合鍵的接口會產生一個聯合值。 但是要從鍵的聯合中以編程方式進行交集,需要更多的類型雜耍。 當您在具有多個推理候選的逆變position 中的條件類型中使用infer
時,會自動為您生成交點。 假設您使用的是--strictFunctionTypes
編譯器選項(它也記錄了我所說的“逆變”的意思,但也可以在這里查看),其中一個這樣的 position 是 function 的參數。
對於傳入的鍵,我們創建一個映射類型,我們將相應的*Return
類型放入逆變 position 中,然后infer
這些類型的並集,這會產生一個交集。 這也是用於將並集轉換為交集的通用方法。
讓我們看看它是否有效:
const regularArray = createArray([]) // any[]
const alsoRegularArray = createArray([], {}) // any[]
const distantReturn = createArray([], { distant: true }) // any[] & DistantReturn
const selectableReturn = createArray([], { selectable: true })
// any[] & SelectableReturn
const distantAndSelectableReturn =
createArray([], { selectable: true, distant: true })
// any[] & SelectableReturn & DistantReturn
看起來不錯!
而且它很容易擴展; 您所要做的就是向CreateArrayOptions
添加更多條目:
interface CreateArrayOptions {
selectable: SelectableReturn;
distant: DistantReturn;
thirdThing: ThirdThingReturn;
}
const somethingElse = createArray([], { thirdThing: true, distant: true });
// const somethingElse: any[] & DistantReturn & ThirdThingReturn
somethingElse.monkeys // boolean
somethingElse.distances // number[]
對於您的最后一個問題,如果 IntelliSense 不適合您,這似乎是 TypeScript 中的錯誤或設計限制或缺少功能,其中泛型類型阻止生成合理的 IntelliSense 完成列表。 GitHub 中有很多關於 IntelliSense 缺點的問題,但我不知道我是否找到了正是這個問題的問題。 就像, microsoft/TypeScript#28662看起來相關但並不明顯相同。 無論如何,在microsoft/TypeScript#28470中,有一條注釋顯示了一種解決方法,在這里翻譯它會產生以下調用簽名:
declare function createArray<K extends keyof CreateArrayOptions = never>(
arr: any[],
options?: Record<K, true> & Partial<Record<keyof CreateArrayOptions, true>>
): any[] & Return<K>;
我們希望將options
視為可分配給Partial<Record<keyof CreateArrayOptions, true>>
,原始版本的CreateArrayOptions
看起來像{selected?: true; distant?: true}
{selected?: true; distant?: true}
( Partial<T>
實用程序類型使所有屬性都是可選的),但仍然讓我們推斷K
。 那個路口對我們都有好處。
所以現在你應該得到一些 IntelliSense,它至少知道預期的鍵,它應該看起來像這樣:
// createArray([], { })
// ---------------> |
// ◾ distant (property) distant?: true | undefined
// ◾ selectable (property) selectable?: true | undefined
或者對於那些可以看到圖像的人:
萬歲!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.