TypeScript 型別挑戰:元組轉換為物件

語言: CN / TW / HK

高質量的型別可以提高專案的可維護性並避免一些潛在的漏洞。

一些前端面試中考察到了 TypeScript 高階型別的定義,本系列主要解答來自 Type Challenges 專案中的 TS 型別挑戰問題,以此更好的瞭解 TS 的型別系統,編寫自己的型別工具,更好的應對前端面試。

下面來看一個難度為簡單的題目:元組轉換為物件

題目描述

傳入一個元組型別,將這個元組型別轉換為物件型別,這個物件型別的鍵/值都是從元組中遍歷出來。

例如:

const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const

type result = TupleToObject<typeof tuple> 
// expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}

題目解答

我們需要從陣列中獲取所有值,並將其作為新物件中的鍵和值。

首先我們知道什麼是元組,來看TypeScript 對元組的定義:

元組型別是另一種Array型別,它確切地知道包含多少個元素,以及它在特定位置包含哪些型別。

這意味著我們可以檢查length並得到確切的數字:

const fullName:[first: string, last: string] = ['hello', 'world'];
const range:[start: number, end: number] = [0, 10];
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] as const;

type FullNameLength = (typeof fullName)['length'] // 2
type RangeLength = (typeof range)['length']       // 2
type DigitsLength = (typeof digits)['length']     // 10

而在陣列中就無法實現這一點:

const fullName:string[] = ['hello', 'world'];
const range:number[] = [0, 10];

type FullNameLength = (typeof fullName)['length'] // number
type RangeLength = (typeof range)['length']       // number

可以使用對映型別來遍歷物件:

type MappedType<T> = {
  [Key in keyof T]: T[Key];
};
  • keyof T用於從物件型別T中獲取鍵值 key;
  • in用於對物件鍵值key進行迭代;
  • Key 就是物件鍵值 key 本身;
  • T[Key]是指定 Key 的值;

我們使用索引訪問型別來遍歷元組,可以通過T[number]從元組中獲取值。具體實現如下:

type TupleToObject<T> = {
    [Value in T[number]]: Value;
};
  • T[number] 用於從元組 T 中獲取值;
  • in 用於迭代元組值;
  • Value 是元組元素,用作構建物件的key和value。

但是這時候報錯了:

這時就需要約束泛型的型別,最終的實現如下:

type TupleToObject<T extends readonly any[]> = {
    [Value in T[number]]: Value;
};

這裡的extends readonly any[] 是呼叫T[number] 所必須的,用來約束 T 的型別,T是一個元組,元組元素是隻讀的。

Type Challenges:https://github.com/type-challenges/type-challenges