Bootstrap

索引签名的使用及松散索引签名

索引签名的使用及松散索引签名

索引签名基本使用

interface IIterator {
  // 索引签名
  [index: string]: number;
  // 该属性受索引签名约束,
  // 如果返回其他类型,则会报错
  length: number;
}

type IteratorType = {
  [index: number]: string;
  // 这里index为number,这里没报错的原因是,数组默认原型包含length字段
  length: number;
};
const test1: IIterator = {
  age: 23,
  price: 18.9,
  length: 2,
};
const test2: IteratorType = ["1", "2"];

js中数组下表会转成string类型

原文(官方文档):JavaScript will actually convert that to a string before indexing into an object. That means that indexing with 100 (a number) is the same thing as indexing with "100" (a string), so the two need to be consistent.

文档链接:https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures

翻译:js实际上会在索引到对象之前将其转换为字符串,也就死说100(number)进行索引与"100"(string)进行索引是一回事,因此两者(两种索引签名)需要保持一致。

同时包含两种(number/string)的索引签名

class Animal {
  constructor(public age: number) {}
}
class Pig extends Animal {
  constructor(public name: string, age: number) {
    super(age);
  }
}

interface IBothIndexIterator {
  [index: number]: Pig; // 索引类型为number的返回值类型,必须为必须为Animal or Animal的子类型
  [index: string]: Animal;
}

// ❌ 错误演示
interface IErrorBothIndexIterator {
  [index: number]: object; // ❌ 索引类型为number的返回值类型不是Animal,反而是Animal的父类型
  [index: string]: Animal;
}

奇怪的现象:为什么返回值为any时,不在校验"数组"的索引类型了?

  • 默认所有的索引签名都是严格的字面量检测,不符合条件的都会报错!
  • 也就是说,即使js会将number covert to string(0 -> “0”),但是字面量严格检测只会将它视为number类型!
// 索引签名数组问题 - [index: string]: string
interface IIndex {
  [index: string]: string;
}
// 尝试将arr改为非fresh新鲜的,扩展字面量
const arr = ["1", "2"];
// ❌ 字面量严格类型检测,索引默认为number无法通过
const arr1: IIndex = arr;
// ❌ 同上错误
const arr2: IIndex = ["1", "2"];
// ✅ 符合类型
const obj1: IIndex = {
  "0": "0",
  "2": "1",
};
// ✅ 符合类型,不要以为写的0就是个number了,实际上object的key都是string类型,所以这里通过
const obj2: IIndex = {
  0: "0",
  1: "1",
};

这里都很好理解,我要讲的是下面这个现象,类型检测居然不会检测索引类型了?🤔

  • 奇怪的现象🤔
// 奇怪的现象:[index: string]: any;
interface IIndexInattentive {
  [index: string]: any;
}
// ✅ 都通过🤔
const arr1: IIndexInattentive = ["1", "2"];
const obj1: IIndexInattentive = {
  0: "0",
  1: "1",
};

这里就仿佛根本没有对索引进行检索

  • 论点:是否因为字面量包含了函数等方法,导致不通过呢?❌
// 奇怪的现象论点:字面量隐式包含函数类型 ❌
interface IIndexSigWithFunc {
  [index: string]: string | ((...args: any[]) => any);
}
// ❌
const arr1: IIndexSigWithFunc = ["1", "2"];
// ✅
const obj1: IIndexSigWithFunc = {
  "0": "0",
  "2": "1",
  "3"() {},
};

破案奇怪的现象

I think that's a good assumption, I can't think of a scenario where the target type would want to mandate an index signature explicitly. The index signature of type of any or empty object would then always signify a loose check, making the above code pass without error - correct?

翻译:我认为这是一个很好的假设,我想不出目标类型想要明确要求索引签名的场景。然后,any类型或空对象的索引签名将始终表示松散的检查,从而使上述代码无错误地通过

  • 由官方的描述可以得出以下宽松索引签名的触发条件
    1. 索引签名返回(指向)any类型
    2. 对于空对象的索引签名默认为宽松
// 奇怪的现象:[index: string]: any; 宽松索引签名
// 方式一:any触发条件的宽松索引签名
interface IIndexInattentive {
  [index: number]: any;
}
// ✅ 你会发现它们不再检查索引类型,完全不检查了
const arr1: IIndexInattentive = ["1", "2"];
const obj1: IIndexInattentive = {
  0: "0",
  1: "1",
  [Symbol("2")]: () => {},
};

// 方式二:空对象触发条件的索引签名
// ✅ 都通过
let obj3: {} = ["1", "2", 3, true];
obj3 = {
  k: "v",
  "0": 0,
  1: "1",
  // 甚至symbol都能通过
  [Symbol("3")]: () => {},
};

总结:宽松索引签名,是为了特定场景,如框架的类型定义,不用因为过于严格,而导致类型定义起来特别麻烦

参考文档

;