在没有非常了解 Record 之前,定义对象的类型,一般使用 interface。它是 TS 中定义数据结构的一种方式,用来描述对象的形状、函数类型、类的结构等。
// 基本用法
interface User {
  name: string;
  age: number;
  isAdmin: boolean;
}
const user: User = {
  name: "Alice",
  age: 30,
  isAdmin: true,
}; 
而在 TypeScript 中,提供了一个实用工具类型 --- Record,用于构建对象类型的映射。它允许我们创建一个对象类型,其中所有键的类型和值的类型都支持自定义。
1. 基本用法
Record<K, T> 类型有两个参数:
- K:键的类型(通常是 string、number 或 symbol,或者它们的联合类型)
- V:值的类型
举个 🌰
type UserRoles = 'admin' | 'user' | 'guest';
type RolePermissions = Record<UserRoles, string[]>;
const permissions: RolePermissions = {
  admin: ['read', 'write', 'delete'],
  user: ['read', 'write'],
  guest: ['read']
}; 
Record<UserRoles, string[]> 定义一个对象类型,其中键必须是 'admin'、'user' 或者 guest',而值是字符串数组。
2. 注意事项
1、键的类型
Record<K, T> 要求键的类型必须是 string、number 或 symbol 类型的子类型。因此不能使用 boolean 或 自定义对象作为键类型。
错误 🌰
type InvalidRecord = Record<boolean, string>; 

2、覆盖问题
如果 K 类型是联合类型,Record 会要求每个键都必须出现在对象中。这意味着即使我们只需要部分键,但是必须声明所有键。
举个 🌰
type Status = 'success' | 'error' | 'pending';
const statusMessages: Record<Status, string> = {
  success: 'Operation was successful',
  error: 'There was an error',
  pending: 'Operation is pending',
}; 
若是某一项不写,vscode 会给与提示信息。

3. 与 interface 对比
1、联系
二者都可用于描述对象结构,确保对象属性符合指定类型。
2、区别
1)用途与场景
如果需要定义一个具有复杂结构、方法或需要被多个类实现的类型,选择 interface。
如果是用来定义一组键值对,尤其是当键的类型可以枚举或确定是,选择 Record 更加简洁。
2)扩展性
interface 可扩展,支持继承,通过 extends 关键字扩展已有接口,从而增加或修改属性。
Record 不可扩展,只是一个工具类型。
举个 🌰
interface Animal {
  name: string;
}
interface Dog extends Animal {
  breed: string;
}
const myDog: Dog = {
  name: "Buddy",
  breed: "Golden Retriever"
}; 
Record 无法直接扩展,但可以使用交叉类型(&)将 Record 和另一个类型结合。
type Animal = {
  name: string;
};
type Dog = Record<"breed", string> & Animal;
const myDog: Dog = {
  name: "Buddy",
  breed: "Golden Retriever"
}; 
3)描述方法
Record 不能描述对象的方法,仅用于定义键值对类型,因此定义方法需要使用 interface。
interface Person {
  name: string;
  getName(): string;
  setName(value: string): void;
}
let person: Person = {
  name: "Alice",
  getName: function () {
    return this.name;
  },
  setName: function (value: string) {
    this.name = value;
  }
}; 
4)声明合并
TypeScript 支持接口的声明合并,这意味同名接口会自动合并属性,而 Record 做不到。
举个 🌰
// 假设我们有一个接口
interface LibraryBook {
  title: string;
  author: string;
}
// 继续扩展这个接口
interface LibraryBook {
  publishedYear: number;
}
// 现在 LibraryBook 接口同时拥有了 title, author 和 publishedYear 属性 
// Record 不支持声明合并
type BookRecord = Record<string, any>;
// 下面的声明不会合并到 BookRecord,而是创建了一个新的类型别名
type BookRecord = {
  publishedYear: number;
};
                


















