Typescript 深入講解可索引類型 Indexable Types

1. 準備工作

npm init -y
npm install ts-node typescript

2. Typescript可索引類型

Typescript同時支持number和string類型的索引類型。

2.1 number可索引類型

2.1.1 Sample Code

interface NumIndexType {
    [numIdx: number]: any;
}

let myVar: NumIndexType;

myVar = ["elem1", "eleme2", "elem3"];

console.log('-------> type: ' + (typeof myVar));
console.log('-------> instanceof Array?: ' + (myVar instanceof Array));

console.log('-------> toString: ' + myVar.toString());

console.log('-------> iterate: ');

for (let v  in myVar) {
    console.log(v);
}

2.1.2 Result

編譯執(zhí)行: npx ts-node test.ts
執(zhí)行結果:

-------> type: object
-------> instanceof Array?: true
-------> toString: elem1,eleme2,elem3
-------> iterate:
0
1
2

2.1.3 結果分析

  • 實例一個數組,JS中數組是繼承Object的。
  • 調用toString()可以將元素輸出。
  • number可索引類型其實就跟普通的數組一樣,不難理解。

2.2 string可索引類型

2.2.1 Sample Code

interface NumIndexType {
    [numIdx: number]: any;
    [strIdx: string]: any;
}

let myVar: NumIndexType;

myVar = ["elem1", "eleme2", "elem3"];

myVar["strIdx0"] = "strIdx0:0";
myVar["strIdx1"] = "strIdx1:1";

console.log('-------> type: ' + (typeof myVar));
console.log('-------> instanceof Array?: ' + (myVar instanceof Array));

console.log('-------> toString: ' + myVar.toString());

console.log('-------> iterate: ');

for (let v  in myVar) {
    console.log(v);
}

console.log('array length: ' + myVar.length);


console.log('--------> 我是分割線 -------->');

console.log("myVar['strIdx0']: " + myVar["strIdx0"]);
console.log("myVar['strIdx1']: " + myVar["strIdx1"]);
console.log('toStirng(): ' + myVar.toString());

console.log(myVar.strIdx0);

console.log('array length: ' + myVar.length);

2.2.2 Result

-------> type: object
-------> instanceof Array?: true
-------> toString: elem1,eleme2,elem3
-------> iterate:
0
1
2
strIdx0
strIdx1
array length: 3
--------> 我是分割線 -------->
myVar['strIdx0']: strIdx0:0
myVar['strIdx1']: strIdx1:1
toStirng(): elem1,eleme2,elem3
strIdx0:0
array length: 3

2.2.3 結果分析

  • 同時支持number和string可索引類型,返回的object仍然是Array。
  • string可索引類型,賦值不能在toString輸出。
  • 用數組去迭代,或者求數組的length,顯示用字符串下標并沒有影響Array的length,索引。
  • string下標賦值,其實是給Array的原型增加了新的屬性,所以應用數組的方法不被應用也不起作用。

2.2.4 number索引類型、其他屬性如何與string索引類型共存

2.2.4.1 如果同時有number和string索引類型,那么number索引類型的返回值類型必須是string索引類型的子類

如下代碼(摘自官方文檔,但是不妨礙老張解釋):

class Animal {
    name: string;
}

class Dog extends Animal {
    breed: string;
}

interface NotOkay {
    [x: number]: Animal;
    [x: string]: Dog;
}

解釋:

  • Dog 是Animal的子類
  • number 索引的返回類型是Animal, string索引的返回類型是Dog。
  • 按照理論來講,number索引的返回類型應該是string索引的返回類型的子類。明顯我們這里Animal不是Dog子類,所以這個會編譯錯誤。錯誤如下,提示信息很明確指出錯誤原因:
? Unable to compile TypeScript:
Animal.ts:10:2 - error TS2413: Numeric index type 'Animal' is not assignable to string index type 'Dog'.

10  [x: number]: Animal;
    ~~~~~~~~~~~~~~~~~~~~

2.2.4.2 在一個interface中,只定義了string類型索引,對其他的屬性有什么約束?

  • 基于之前的知識,應該很容易理解,定義了string類型的索引,其實就是約束了這個類的屬性的類型。比如定義了如下的interface(依然是官方sample,但是仍然不妨礙舉例):
interface NumberOrStringDictionary {
    [index: string]: number ;
}

上面的代碼片段,定義類一個string類型索引,返回值類型是number,那么其實就是限定了這個類的其他屬性也是number或其子類型(這個其實就是設計模式的里式替換原則,父類可以在的地方,就可以用子類代替)。

  • 基于如上解釋,我們有如下代碼片段:
interface NumberOrStringDictionary {
    [index: string]: number;
    length: number;
    name: string;
}

按理論,所有非索引類型屬性,類型必須是number或number的子類。顯然,name屬性是錯誤的。編譯會有如下錯誤:

Animal.ts:4:5 - error TS2411: Property 'name' of type 'string' is not assignable to string index type 'number'.

4     name: string;
      ~~~~
  • 如何解決上面類型不兼容的問題?
    可以通過typescript的復合類型解決。將索引類型的返回類型定義為number| string;
interface NumberOrStringDictionary {
    [index: string]: number| string;
    length: number;
    name: string;
}

再次將不會出現錯誤。

3. Summary

  • JS數組添加字符串下標的時候,其實就相當于為該數組對象添加了一個屬性,屬性名稱就是我們所謂的“字符串下標”。
  • 由于為數組對象添加屬性不會影響到同為該對象屬性的length的值,因此Array的length值只受數字下標的影響。
  • pop(), shift()等數組方法是應用數組的,所以對字符串下標的屬性不起作用。
  • 如果是完全string數組下標的數組,還是用Object吧。
  • 數組下標值的范圍為0到2的32次方, 對于任意給定的數字下標值,如果不在此范圍內,js會將它轉換為一個字符串,并將該下標對應的值作為該數組對象的一個屬性值而不是數組元素,例如array[-1] = "yes" 其實就相當于給array對象添加了一個名為-1的屬性,屬性值為yes。
  • 如果該下標值在合法范圍內,則無論該下標值是數字還是數字字符串,都一律會被轉化為數字使用,即 array["100"] = 0 和 array[100] = 0 執(zhí)行的是相同的操作。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容