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í)行的是相同的操作。