說說我對 TypeScript 索引簽名 理解

前端小智發表於2021-10-20
作者:Dmitri Pavlutin
譯者:前端小智
來源:dmitripvlutin

有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。

本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

我們用兩個物件來描述兩個碼農的工資:

const salary1 = {
  baseSalary: 100_000,
  yearlyBonus: 20_000
};
 
const salary2 = {
  contractSalary: 110_000
};

然後寫一個獲取總工資的函式

function totalSalary(salaryObject: ???) {
  let total = 0;
  for (const name in salaryObject) {
    total += salaryObject[name];
  }
  return total;
}
totalSalary(salary1); // => 120_000
totalSalary(salary2); // => 110_000

如果是你的,要如何宣告totalSalary()函式的salaryObject引數,以接受具有字串鍵和數字值的物件?

答案是使用一個索引簽名!

接著,我們來看看什麼是 TypeScript 索引簽名以及何時需要它們。

1.什麼是索引簽名

索引簽名的思想是在只知道鍵和值型別的情況下對結構未知的物件進行型別劃分。

它完全符合salary引數的情況,因為函式應該接受不同結構的salary物件,唯一的要求是屬性值為數字。

我們用索引簽名來宣告salaryObject引數

function totalSalary(salaryObject: { [key: string]: number }) {
  let total = 0;
  for (const name in salaryObject) {
    total += salaryObject[name];
  }
  return total;
}
 
totalSalary(salary1); // => 120_000
totalSalary(salary2); // => 110_000

{[key: string]: number} 是索引簽名,它告訴TypeScript salaryObject 必須是一個以string 型別為鍵,以 number 型別為值的物件。

2. 索引簽名語法

索引簽名的語法相當簡單,看起來與屬性的語法相似,但有一點不同。我們只需在方括號內寫上鍵的型別,而不是屬性名稱:{ [key: KeyType]: ValueType }

下面是一些索引簽名的例子。

string 型別是鍵和值。

interface StringByString {
  [key: string]: string;
}
 
const heroesInBooks: StringByString = {
  'Gunslinger': '前端小智',
  'Jack Torrance': '王大志'
};

string 型別是鍵,值可以是 stringnumberboolean

interface Options {
  [key: string]: string | number | boolean;
  timeout: number;
}
 
const options: Options = {
  timeout: 1000,
  timeoutMessage: 'The request timed out!',
  isFileUpload: false
};

簽名的鍵只能是一個 string`、numbersymbol`。其他型別是不允許的。

image.png

3. 索引簽名的注意事項

TypeScript中的索引簽名有一些注意事項,需要注意。

3.1不存在的屬性

如果試圖訪問一個索引簽名為 { [key: string]: string } 的物件的一個不存在的屬性,會發生什麼?

正如預期的那樣,TypeScript 將值的型別推斷為 string。但是檢查執行時值,它是undefined:

image.png

根據 TypeScript 提示, value變數是一個 string 型別,但是它的執行時值是 undefined

索引簽名只是將一個鍵型別對映到一個值型別,僅此而已。如果沒有使這種對映正確,值型別可能會偏離實際的執行時資料型別。

為了使輸入更準確,將索引值標記為 stringundefined。這樣,TypeScript就會意識到你訪問的屬性可能不存在

image.png

3.2 string 和 number 鍵

假設有一個數字名稱的字典:

interface NumbersNames {
  [key: string]: string
}
 
const names: NumbersNames = {
  '1': 'one',
  '2': 'two',
  '3': 'three',
  // ...
};

通過字串鍵訪問值與預期一致:

image.png

如果用數字 1 訪問一個值會出錯嗎?

image.png

不會,正常工作。

當在屬性訪問器中作為鍵使用時,JavaScript隱式地將數字強制為字串(names[1]names['1']相同)。TypeScript也會執行這個強制。

你可以認為 [key: string][key: string | number] 相同。

4.索引簽名與 Record<Keys, Type>對比

TypeScript有一個實用型別 Record<Keys, Type>,類似於索引簽名。

const object1: Record<string, string> = { prop: 'Value' }; // OK
const object2: { [key: string]: string } = { prop: 'Value' }; // OK

那問題來了...什麼時候使用 Record<Keys, Type>,什麼時候使用索引簽名?乍一看,它們看起來很相似

我們知道,索引簽名只接受 stringnumbersymbol 作為鍵型別。如果你試圖在索引簽名中使用,例如,字串字面型別的聯合作為鍵,這是一個錯誤。

image.png

索引簽名在鍵方面是通用的。

但是我們可以使用字串字面值的聯合來描述 Record<keys, Type>中的鍵

type Salary = Record<'yearlySalary'|'yearlyBonus', number>
 
const salary1: Salary = { 
  'yearlySalary': 120_000,
  'yearlyBonus': 10_000
}; // OK

Record<Keys, Type> 是為了具體到鍵的問題。

建議使用索引簽名來註釋通用物件,例如,鍵是字串型別。但是,當你事先知道鍵的時候,使用Record<Keys, Type>來註釋特定的物件,例如字串字面量'prop1' | 'prop2'被用於鍵值。

總結

如果你不知道你要處理的物件結構,但你知道可能的鍵和值型別,那麼索引簽名就是你需要的。

索引簽名由方括號中的索引名稱及其型別組成,後面是冒號和值型別:{ [indexName: KeyType]: ValueType }KeyType 可以是一個 stringnumbersymbol,而ValueType 可以是任何型別。

~~完,我是刷碗智,寫到這海賊王最新一畫《天王山》更新了超級刺激,我要去追了,我們下期見!

編輯中可能存在的bug沒法實時知道,事後為了解決這些bug,花了大量的時間進行log 除錯,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug

原諒:https://dmitripavutin.com/typ...

交流

文章每週持續更新,可以微信搜尋【大遷世界 】第一時間閱讀,回覆【福利】有多份前端視訊等著你,本文 GitHub https://github.com/qq449245884/xiaozhi 已經收錄,歡迎Star。

相關文章