TS之索引签名详解

1,706 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第16天,点击查看活动详情

一. 什么是所有签名?

以用字符串访问 JavaScript 中的对象(TypeScript 中也一样),用来保存对其他对象的引用。比如数组或对象对应位置的名字。

举例:

let foo: any = {};
foo['Hello'] = 'World';
console.log(foo['Hello']); // World

提示:TS的索引签名必须是 string 或者 numbersymbols 也是有效的,TS 支持它。

二. 使用场景

1. number类型索引签名:用来约束数组

image.png image.png

2. string类型索引签名:用于约束对象

const foo: {
  [index: string]: { message: string };
} = {};

// 储存的东西必须符合结构
// ok
foo['a'] = { message: 'some message' };

// Error, 必须包含 `message`
foo['a'] = { messages: 'some message' };

// 读取时,也会有类型检查
// ok
foo['a'].message;

// Error: messages 不存在
foo['a'].messages;

索引签名的名称(如:{ [index: string]: { message: string } } 里的 index )除了可读性外,并没有任何意义。例如:如果有一个用户名,你可以使用 { username: string}: { message: string },这有利于下一个开发者理解你的代码。

3. 同时使用 string 和 number 类型的索引签名:不常用

举例1:

image.png

image.png

正确改为:

image.png

举例2:

image.png

image.png 正确改为:

image.png

由上图例子可以看出:

可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型

原因是当使用 number 来索引时,JavaScript 会将它转换成 string 然后再去索引对象。

三. 注意事项

1. 声明一个索引签名时,所有明确的成员都必须符合索引签名

// ok
interface Foo {
  [key: string]: number;
  x: number;
  y: number;
}

// Error
interface Bar {
  [key: string]: number;
  x: number;
  y: string; // Error: y 属性必须为 number 类型
}

这可以给你提供安全性,任何以字符串的访问都能得到相同结果

interface Foo {
  [key: string]: number;
  x: number;
}

let foo: Foo = {
  x: 1,
  y: 2
};

// 直接
foo['x']; // number

// 间接
const x = 'x';
foo[x]; // number

2. 一个索引签名可以通过映射类型来使索引字符串为联合类型中的一员

type Index = 'a' | 'b' | 'c';
type FromIndex = { [k in Index]?: number };

const good: FromIndex = { b: 1, c: 2 };

// Error:
// `{ b: 1, c: 2, d: 3 }` 不能分配给 'FromIndex'
// 对象字面量只能指定已知类型,'d' 不存在 'FromIndex' 类型上
const bad: FromIndex = { b: 1, c: 2, d: 3 };

提示:映射类型:基于旧类型创建新类型(对象类型),减少重复、提升开发效率