Open Index Signature

還記得在 Java 的進階用法 Reflection 嗎?

其實在 TypeScript 開放索引簽名 就有一點點相似於 Java 中的反射概念,

因為它們都有提供動態地添加物件屬性的方法,

而不需要事先定義固定的結構,

舉例來說,

在 Typescript 中, 我們可以使用 interface 來定義一個物件的型別:

interface Person {
  name: string;
  age: number;
}

但是如果我們想要定義一個物件的型別,

但是不確定它的屬性有哪些,

可以使用 Open Index Signature:

interface Person {
  [key: string]: string | number;
}

這樣就可以定義一個物件的型別,

但是不確定它的屬性有哪些

定義

索引簽名的定義長這兩個樣子:

// 透過字串
{
    [index: string]: type;
}

// 透過數字
{
    [index: number]: type;
}

index 可以是字串或是數字,

type 可以是任何型別,

只能 選擇一種索引簽名 來定義物件的型別,

不能同時使用字串和數字

字串索引簽名

使用字串作為索引來訪問對象的屬性,

通常用來動態地添加屬性

interface Cat {
  [key: string]: string;
}

const cat: Cat = {};

cat["name"] = "Kitty";
cat["age"] = "1";

console.log(cat["name"]); // Kitty

數字索引簽名

使用數字作為索引來訪問對象的屬性,

通常用來處理陣列

interface CatArray {
  [key: number]: string;
}

const catArray: CatArray = [];

catArray[0] = "Kitty";
catArray[1] = "Judy";

console.log(cat[0]); // Kitty

透過字串索引簽名訪問屬性

可以先定義一個物件的型別:

interface Person {
  name: string;
  age: number;
  [key: string]: string | number;
}

然後再透過字串索引簽名來訪問屬性:

const person: Person = {
  name: "John",
  age: 18,
};

console.log(person.name); // John
console.log(person["age"]); // 18

person.gender = "male"; // 可以動態地添加屬性

console.log(person.gender); // male

readonly

可以使用 readonly 關鍵字來設定屬性為唯讀:

interface Person {
  readonly name: string;
  readonly age: number;
  [key: string]: string | number;
}

const person: Person = {
  name: "John",
  age: 18,
};

person.name = "Mary"; // 會報錯

物件比對運用字串索引簽名

這是一個將物件的屬性排序的範例,

直接比對物件的屬性會發現,

雖然屬性的值都相同,

但因為順序不同所以比對結果會是 false,

但是如果先將物件的屬性排序,

並運用字串索引簽名的方式獲取該物件屬性,

動態添加排序後的屬性給新的物件,

即可以得到正確的結果

// 索引簽名範例
const c: { [key: string]: number } = { a: 1, b: 2 };
const d: { [key: string]: number } = { b: 2, a: 1 };
const sortC: { [key: string]: number } = {};
const sortD: { [key: string]: number } = {};

Object.keys(c)
  .sort()
  .forEach((key) => {
    sortC[key] = c[key];
  });

Object.keys(d)
  .sort()
  .forEach((key) => {
    sortD[key] = d[key];
  });

console.log(JSON.stringify(c) === JSON.stringify(d)); // false
console.log(JSON.stringify(sortC) === JSON.stringify(sortD)); // true

這邊有個小技巧, 就是使用 JSON.stringify() 來比對兩個物件是否相等

參考資料