Typescript 深入讲解可索引类型 Indexable T
2020-03-20 本文已影响0人
张毅_f72c
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
编译执行: npx ts-node test.ts
执行结果:
-------> 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 执行的是相同的操作。