首頁 > web前端 > js教程 > 主體

分享一個不錯的TS面試題(含3層),看看能答到第幾層!

青灯夜游
發布: 2023-01-14 19:36:19
轉載
2691 人瀏覽過

分享一個不錯的TS面試題(含3層),看看能答到第幾層!

最近遇見一個不錯的 TS 面試題,分享一下。

這題有 3 個層次,我們一層層來看。

第一層的要求是這樣的:

實作一個zip 函數,對兩個陣列的元素依序兩兩合併,例如輸入[1,2,3] , [4,5,6] 時,返回[[1,4], [2,5],[3,6]]

這層就是每次各從兩個陣列取一個元素,合併之後放到陣列裡,然後繼續處理下一個,遞歸進行這個流程,直到陣列為空即可。

function zip(target, source) {
  if (!target.length || !source.length) return [];

  const [one, ...rest1] = target;
  const [other, ...rest2] = source;

  return [[one, other], ...zip(rest1, rest2)];
}
登入後複製

結果是對的:

第一層還是比較簡單的,然後我們來看第二層要求:

#給這個zip 函數定義ts 類型(兩種寫法)

函數的定義有兩種形式:

#直接透過function 宣告函數:

function func() {}
登入後複製

和宣告匿名函數接著賦值給變數:

const func = () => {}
登入後複製

而參數和傳回值的型別都是數組,只是具體型別不知道,可以寫unknown[]。

所以兩個函數類型的定義就是這樣的:

#也是直接function 宣告函數類型和interface 聲明函數類型接著加到變數類型上兩種。

因為具體元素類型不知道,所以用 unknown。

這裡可能會問any 和unknown 的差別:

any 和unknown 都可以接收任何型別:

但是any 也可以賦值給任何類型,但unknown 不行。

這裡只是用來接收其他類型, 所以 unknown 比any 更合適一些,更安全。

這一層也是比較基礎的ts 語法,第三層就上了難度了:

用型別程式實作精確的型別提示,例如參數傳入[1, 2,3], [4,5,6],那回傳值的型別要提示出[[1,4], [2,5],[3,6]]

這裡要求回傳值類型是精確的,我們就要根據參數的類型來動態產生回傳值類型。

也就是這樣:

宣告兩個型別參數 Target、Source,約束為 unknown[],也就是元素型別任意的陣列型別。

這兩個類型參數分別是傳入的兩個參數的型別。

傳回值透過 Zip 計算得出。

接著要實現 Zip 的高階類型:

傳入的型別參數分別是兩個陣列類型,我們同樣要從中提取每個元素合併在一起。

提取元素可以用模式匹配的方式:

所以這個類型就可以這樣定義:

type Zip<One extends unknown[], Other extends unknown[]> =
    One extends [infer OneFirst,...infer Rest1]
      ? Other extends [infer OtherFirst, ...infer Rest2]
        ? [[OneFirst, OtherFirst], ...Zip<Rest1, Rest2>]
        : []
      : [];
登入後複製

分別擷取兩個陣列的第一個元素,建構成新陣列。然後對剩下的數組遞歸進行這樣的處理,直到數組為空。

這樣就實現了我們想要的高階類型:

但你把它加到函數上會錯誤是回傳值:

因為宣告函數的時候都不知道參數是啥,自然計算不出Zip 的值,所以這裡會類型不符:

#那該怎麼辦?

可以用函數重載解決:

ts 支援函數重載,可以寫多個同名函數的類型的類型定義,最後寫函數的實現,這樣用到這個函數的時候會根據參數的型別來匹配函數型別。

我們用了類型程式設計的那個函數透過這種方式寫就不會報錯了。

我們使用下來看看:

#

咋回傳值的型別不對呢?

其實這時候匹配的函數型別是對的,只不過推導出的不是字面量類型。

這時候可以加個 as const。

但加上as const 會推導出readonly [1,2,3]

這樣型別就不符合了,所以要在型別參數的宣告上也加上readonly:

但這樣Zip 函數的型別又不匹配了。

難道要把所有用到這個類型的地方都加上 readonly 麼?

不用,我們 readonly 的修飾去掉不就行了?

Typescript 有內建的高階類型readonly:

#可以把索引類型的每個索引都加上readonly 修飾:

但沒有提供去掉readonly 修飾的高階類型,我們可以自己實作:

用對映類型的語法建構個新索引類型,加上個-readonly 就是去掉readonly 修飾的意思。

有的同學可能問了,陣列型別也是索引型別?

是,索引類型是聚合多個元素的類型,所以物件、陣列、class 都是。

所以我們把它用在數組上自然也是可以的:

#(準確來說叫元組,元組是元素個數固定的陣列)

那我們只要在傳入Zip 之前,用Mutable 去掉readonly 就可以了:

再來試試看:

##大功告成!現在傳回值的類型就對了。

但還有個問題,如果不是直接傳入字面量,是推導不出字面量類型的,這時候貌似就不對了:

可我們不都宣告重載類型了麼?

如果推導不出字面量類型,應該要匹配這個呀:

但實際上它匹配的還是第一個:

#這時候其實只要調換下兩個函數型別的順序就可以了:

這時字面量參數的情況依然也是對的:

為什麼呢? ######因為###重載函數的型別是從上到下依序匹配,只要匹配到一個就應用。 #########非字面量的情況,型別是 number[],能符合 unknown[] 的那個型別,所以那個函數型別生效了。 ###############而字面量的情況,推導出的是readonly [1,2,3],帶有readonly 所以不匹配unknown[],繼續往下匹配,就匹配到了帶有類型參數的那個函數類型。 ######這樣兩種情況就都應用了合適的函數型別。 ######全部程式碼是這樣的:###
type Zip<One extends unknown[], Other extends unknown[]> = One extends [
  infer OneFirst,
  ...infer Rest1
]
  ? Other extends [infer OtherFirst, ...infer Rest2]
    ? [[OneFirst, OtherFirst], ...Zip<Rest1, Rest2>]
    : []
  : [];

type Mutable<Obj> = {
  -readonly [Key in keyof Obj]: Obj[Key];
};

function zip(target: unknown[], source: unknown[]): unknown[];

function zip<Target extends readonly unknown[], Source extends readonly unknown[]>(
  target: Target,
  source: Source
): Zip<Mutable<Target>, Mutable<Source>>;

function zip(target: unknown[], source: unknown[]) {
  if (!target.length || !source.length) return [];

  const [one, ...rest1] = target;
  const [other, ...rest2] = source;

  return [[one, other], ...zip(rest1, rest2)];
}

const result = zip([1, 2, 3] as const, [4, 5, 6] as const);

const arr1 = [1, 2, 3];
const arr2 = [4, &#39;5&#39;, 6];

const result2 = zip(arr1, arr2);
登入後複製

ts playground 地址

总结

今天我们做了一道综合的 ts 面试题,一共有三层:

第一层实现 js 的逻辑,用递归或者循环都能实现。

第二层给函数加上类型,用 function 声明类型和 interface 声明函数类型两种方式,参数和返回值都是 unknown[]。

第三层是用类型编程实现精准的类型提示,这一层需要拿到参数的类型,通过提取元素的类型并构造出新的数组类型返回。还要通过函数重载的方式来声明类型,并且要注意重载类型的声明顺序。

as const 能够让字面量推导出字面量类型,但会带有 readonly 修饰,可以自己写映射类型来去掉这个修饰。

其实这也是我们学习 ts 的顺序,我们先要能把 js 逻辑写出来,然后知道怎么给函数、class 等加 ts 类型,之后学习类型编程,知道怎么动态生成类型。

其中类型编程是 ts 最难的部分,也是最强大的部分。攻克了这一层,ts 就可以说学的差不多了。

【相关推荐:javascript学习教程

以上是分享一個不錯的TS面試題(含3層),看看能答到第幾層!的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:juejin.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!