在 TypeScript 中,Record 是一个强大的内置工具类型,用于创建 键值映射关系的对象类型。它特别适合需要严格约束对象的 键类型 和 值类型 的场景。以下通过具体场景和示例来详细说明它的用法:
一、基础语法
type Record<K extends keyof any, T> = {
[P in K]: T;
};
K: 表示对象的键类型(必须是string | number | symbol的子类型)T: 表示对象的值类型
二、核心使用场景
1. 定义固定结构的配置对象
// 定义按钮配置类型
type ButtonConfig = Record<"submit" | "cancel" | "reset", {
color: string;
size: number
}>;
// 正确使用
const buttons: ButtonConfig = {
submit: { color: "blue", size: 32 },
cancel: { color: "red", size: 28 },
reset: { color: "gray", size: 28 }
};
// 错误示例:缺少 required 键或类型不匹配
const wrongButtons: ButtonConfig = {
submit: { color: "blue" }, // 错误:缺少 size
unknownKey: { color: "red", size: 28 } // 错误:多余的键
};
2. 动态映射关系
// 将 API 端点映射到 URL
type ApiEndpoints = Record<"user" | "product" | "order", string>;
const apiConfig: ApiEndpoints = {
user: "/api/users",
product: "/api/products",
order: "/api/orders"
};
// 访问时自动推断类型
const userEndpoint = apiConfig.user; // string 类型
3. 替代枚举的键值对
// 比枚举更轻量的状态码映射
type HttpStatus = Record<200 | 404 | 500, string>;
const statusMessages: HttpStatus = {
200: "OK",
404: "Not Found",
500: "Internal Error"
};
// 自动提示键名,避免拼写错误
console.log(statusMessages[404]); // "Not Found"
三、与索引签名的区别
普通索引签名允许任意键名,而 Record 可以更精确控制:
// 索引签名:允许任意 string 键
type LooseObject = {
[key: string]: number;
};
// Record:可限制键名为特定集合
type StrictKeys = Record<"width" | "height", number>;
const obj1: LooseObject = { anyKey: 10 }; // ✅ 允许
const obj2: StrictKeys = { width: 100, height: 200 }; // ✅
const obj3: StrictKeys = { other: 300 }; // ❌ 不允许未知键
四、在 Vue 组件中的应用示例
结合用户之前的错误场景,假设需要定义一个响应式配置对象:
<script lang="ts" setup>
import { reactive } from 'vue';
// 定义配置类型
type ThemeConfig = Record<"primary" | "secondary" | "error", string>;
// 使用 Record 约束响应式对象
const theme = reactive<ThemeConfig>({
primary: "#409EFF",
secondary: "#67C23A",
error: "#F56C6C"
});
// 错误示例:缺少 required 键
const wrongTheme = reactive<ThemeConfig>({
primary: "blue" // ❌ 缺少 secondary 和 error
});
</script>
五、高级用法
1. 联合类型键的动态生成
type Keys = "id" | "createdAt";
type Entity = Record<Keys, string> & {
// 可扩展其他属性
metadata?: object;
};
const user: Entity = {
id: "001",
createdAt: "2023-01-01",
metadata: { role: "admin" }
};
2. 递归 Record 结构
// 定义树状结构
type Tree<T> = Record<string, T | Tree<T>>;
const categoryTree: Tree<number> = {
electronics: {
phones: 120,
laptops: 85
},
books: 45
};
六、为何能避免用户之前的错误?
用户之前的错误包含 Parsing error: Missing initializer in const declaration,这通常是由于 TypeScript 未能正确解析类型导致的。使用 Record 可以:
- 明确类型结构:提前定义对象应有的键和值类型
- 编译时校验:在编码阶段捕获属性缺失或类型不匹配
- 代码提示增强:IDE 能基于
Record的类型定义提供自动补全
通过合理使用 Record,可以显著提升代码的类型安全性和可维护性,尤其适用于 Vue 组件的 Props 定义、状态管理、配置对象等场景。