這個問題有三個層次,我們壹層壹層來看。
第壹層的要求如下:
實現壹個zip函數來按順序合並兩個數組的元素,例如,當輸入時,它返回,,】
這壹層是每次從兩個數組中取出壹個元素,合並後放入數組中,然後繼續處理下壹個元素,並遞歸地執行此過程,直到數組為空。
functionzip(?目標,來源)?{
如果(!target.length ||!來源。長度)?return【】;
const【one,...rest 1】= target;
const【其他,...rest 2】= source;
返回【【壹,另】,...zip(rest 1,rest 2)】;}
結果是對的。
也有兩種:直接函數聲明函數類型和接口聲明函數類型然後將其添加到變量類型。
因為具體的元素類型未知,所以我們使用unknown。
這裏妳可能會問任何和未知之間的區別:
任何和未知都可以接收任何類型:
但是any可以賦給任何類型,而unknown不能。
這僅用於接收其他類型,因此unknown比任何類型都更合適、更安全。
這壹層也是相對基本的ts語法,第三層更難:
使用類型編程實現準確的類型提示,如參數傳入和應提示的返回值的類型,,】
這裏要求返回值類型準確,所以我們需要根據參數的類型動態生成返回值類型。
就是這樣:
聲明兩個類型參數Target和Source,約束為unknown【】,即任意元素類型的數組類型。
這兩種類型的參數是傳入的兩個參數的類型。
返回值由Zip計算。
然後我們需要實現高級類型的Zip:
傳入的類型參數是兩個數組類型,我們還需要從中提取每個元素並將它們合並在壹起。
可以通過模式匹配提取元素:
因此,這種類型可以定義如下:
typeZip & lt壹個?extendsunknown【】,Other?extendsunknown【】》;=
壹個?擴展【首先推斷壹個,...推斷Rest1】
其他?延伸【推斷他人首先,...推斷剩余2】
【【OneFirst,OtherFirst】,...Zip & ltRest1,Rest2》]
: []
: [];
分別提取兩個數組的第壹個元素來構造新的數組。然後對數組的其余部分遞歸地這樣做,直到數組為空。
這實現了我們想要的高級類型:
但是如果將它作為返回值添加到函數中,將會出現錯誤:
因為當妳聲明壹個函數時妳不知道參數是什麽,妳自然不能計算ZIP
那麽我們該怎麽辦呢?
它可以通過函數重載來解決:
Ts支持函數重載,可以寫多個同名函數的類型定義,最後寫函數的實現,這樣在使用這個函數時,函數類型會根據參數的類型進行匹配。
如果以這種方式編寫,我們使用類型編程的函數將不會報告錯誤。
讓我們使用它來看看:
為什麽返回值的類型不對?
事實上,此時匹配的函數類型是正確的,但它不是文字類型。
此時您可以添加壹個as const。
但是作為常量添加將推導出只讀
因此類型不匹配,所以在類型參數聲明中添加readonly:
但是Zip函數的類型又不匹配。
是否要將readonly添加到所有使用該類型的地方?
不,我們為什麽不刪除對readonly的修改呢?
類型具有內置的高級只讀類型:
索引類型的每個索引都可以用readonly修飾:
但是它沒有提供移除只讀修飾的高級類型。我們可以自己實現它:
用映射類型的語法構造壹個新的索引類型,添加-readonly意味著刪除readonly修飾。
有的同學可能會問,數組類型也是索引類型嗎?
然而,索引類型是聚合多個元素的類型,因此對象、數組和類都是。
因此,我們很自然會在陣列上使用它:
(準確地說,它被稱為元組,即元素數量固定的數組。)
然後我們要做的就是在傳入Zip文件之前用Mutable刪除readonly:
再試壹次:
妳完了!現在返回值的類型是正確的。
但仍有壹個問題。如果不直接傳入字面量,則無法推導出字面量的類型,此時似乎是錯誤的:
但是我們不是都聲明了重載類型嗎?
如果妳不能推斷出文字類型,妳應該這樣匹配:
但它實際上與第壹條相符:
此時,實際上只需改變下兩個函數類型的順序:
此時,文字參數的情況仍然正確:
為什麽?
因為?重載函數的類型從上到下依次匹配,只要有壹個匹配,就會被應用。
在非文字數量的情況下,類型為number【】,可以匹配unknown【】的類型,使函數類型生效。
在文字量的情況下,推斷readonly,with readonly與unknown【】不匹配。如果繼續向下匹配,將使函數類型與類型參數匹配。
在這兩種情況下,都會應用適當的函數類型。
整個代碼如下所示:
typeZip & lt壹個?extendsunknown【】,Other?extendsunknown【】》;=壹個?擴展【
首先推斷壹個,
...推斷Rest1
]
其他?延伸【推斷他人首先,...推斷剩余2】
【【OneFirst,OtherFirst】,...Zip & ltRest1,Rest2》]
: []
: [];
類型可變& ltObj & gt= {
-readonly【Key?in keyof Obj】:Obj【Key】;
};
functionzip(?目標:未知【】,來源:未知【】):?未知【】?;
functionzip<?Targetextendsreadonlyunknown【】,?sourceextendsreadonlyunknown【】& gt;(
目標:目標,
來源:來源
):?Zip & lt?可變& lt?目標& gt,?可變& lt?來源& gt& gt;
functionzip(?目標:未知【】,來源:未知【】)?{
如果(!target.length ||!來源。長度)?return【】;
const【one,...rest 1】= target;
const【其他,...rest 2】= source;
返回【【壹,另】,...zip(rest 1,rest 2)】;
}
constresult = zip(【?1,?2,?3]?阿斯康斯特,【?4,?5,?6]?阿斯康斯特);
constarr1 =【?1,?2,?3];
constarr2 =【?4,?'5',?6];
const result 2 = zip(arr 1,arr 2);
摘要
今天,我們做了壹個全面的ts面試問題,它有三個層次:
第壹層實現js的邏輯,可以通過遞歸或循環來實現。
第二層向函數添加類型,並使用function聲明類型和interface聲明函數類型。參數和返回值都未知【】。
第三層是使用類型編程來實現準確的類型提示。這壹層需要獲取參數的類型,並通過提取元素的類型和構造新的數組類型來返回它們。您還應該通過函數重載來聲明類型,並註意重載類型的聲明順序。
因為const可以使文字推導出文字類型,但它將用readonly修飾,您可以編寫自己的映射類型來消除這種修飾。
其實這也是我們學習ts的順序。我們必須首先能夠編寫js邏輯,然後知道如何將ts類型添加到函數和類中,然後學習類型編程並知道如何動態生成類型。
其中,類型編程是ts最難的部分,也是最強大的部分。攻克這壹層之後,ts可以說是有所了解了。