如何理解 TypeScript 的 Record 类型

远子 â€¢  2021å¹´07月16日

昨天同事问我 TypeScript 的 Record 怎么用的,稍微整理了一下。

假如有这样一份用户数据:

const userMap = {
  zhang_san: { id: 1, code: "001", name: "张三", email: "001@qq.com" },
  li_si: { id: 2, code: "002", name: "李四", email: "002@qq.com" },
  wang_wu: { id: 3, code: "003", name: "王五", email: "" },
};

怎么用 TS 描述 userMap 的类型呢?你可能会这样写:

interface IUser {
  id: number;
  code: string;
  name: string;
  email?: string;
}

interface IUserMap {
  zhang_san: IUser;
  li_si: IUser;
  wang_wu: IUser;
}

const userMap: IUserMap = {
  zhang_san: { id: 1, code: "001", name: "张三", email: "001@qq.com" },
  li_si: { id: 2, code: "002", name: "李四", email: "002@qq.com" },
  wang_wu: { id: 3, code: "003", name: "王五", email: "" },
};

如果 userMap 只需要存储很少量的数据的话,这样写没啥问题,如果有成千上万条呢?

这个时候可以用 Record 类型:

interface IUser {
  id: number;
  code: string;
  name: string;
  email?: string;
}

const userMap: Record<string, IUser> = {
  zhang_san: { id: 1, code: "001", name: "张三", email: "001@qq.com" },
  li_si: { id: 2, code: "002", name: "李四", email: "002@qq.com" },
  wang_wu: { id: 3, code: "003", name: "王五", email: "" },
};

Record<K, T> 是一种对象类型,它接收两个泛型变量,K 代表键的类型,T 是值的类型。

结合上边的例子,Record<string, IUser> 的含义是 userMap 里所有的键都是 string 类型,所有的值都是 IUser 类型。

K 和 T 都可以限制范围,例如:

type IUserEnNameType = 'zhang_san' | 'li_si' | 'wang_wu';
    
interface IUser {
  id: number;
  code: string;
  name: string;
  email?: string;
}

const userMap: Record<IUserEnNameType, IUser> = {
  zhang_san: { id: 1, code: "001", name: "张三", email: "001@qq.com" },
  li_si: { id: 2, code: "002", name: "李四", email: "002@qq.com" },
  wang_wu: { id: 3, code: "003", name: "王五", email: "" },
};

console.log(userMap.zhao_liu); // 报错

上边的代码中,指定了 userMap 的键必须符合 IUserEnNameType 类型,所以在访问 IUserEnNameType 不存在的键(比如 zhao_liu)时会报错:

image-20210716114653694

Record 在描述 object 的时候很有用,你看懂了吗?

(完)