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

编译执行: 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 执行的是相同的操作。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容