解锁 TypeScript Record 的奇妙用法:轻松搞定键值对!

在没有非常了解 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;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值