很抱歉,我似乎无法直接创建文件。这是一个意料之外的限制。
不过,我可以将 TypeScript Record 的详细说明和代码示例直接在这里展示给你。你可以将代码复制粘贴到你自己的 .ts 文件中进行实践。
TypeScript Record 实战:构建灵活且类型安全的对象
在 TypeScript 中,我们经常需要创建结构相似但键名不固定的对象,例如字典、查找表或配置对象。Record<Keys, Type> 是一个内置的工具类型,它为此类场景提供了完美的解决方案,能够帮助我们构建既灵活又类型安全的对象。
Record<Keys, Type> 接受两个泛型参数:
* Keys: 一个由字符串、数字或符号组成的联合类型,或者就是一个 string / number / symbol 类型。它定义了对象允许拥有的键。
* Type: 对象中每个键对应的值的类型。
下面我们通过几个实例来深入理解它的用法。
1. 基本用法:创建一个简单的字典
假设我们需要一个对象来存储一些简单的键值对,比如用字符串作键,用数字作值。
“`typescript
// 定义一个键为任意字符串,值为数字的对象类型
type SimpleObject = Record
const obj1: SimpleObject = {
a: 1,
b: 2,
c: 3,
};
// 类型安全检查:TypeScript 会确保所有值都是数字。
// 下面这行代码会立即引发一个编译错误。
// const invalidObj1: SimpleObject = { a: 1, b: ‘2’ };
// Error: Type ‘string’ is not assignable to type ‘number’.
console.log(‘— 基本用法 —‘);
console.log(obj1);
“`
何时使用:当你需要一个简单的映射或字典,并且所有值都具有相同类型时,这是最直接的用法。
2. 结合联合类型:限制键的范围
Record 的真正威力体现在当你需要精确控制对象的键名时。通过将一个联合类型作为 Keys 参数,你可以创建一个只接受特定键的对象。
“`typescript
// 定义一组明确允许的键
type AllowedKeys = ‘id’ | ‘name’ | ’email’;
// 定义一个 User 类型,它必须包含所有 AllowedKeys,且值都为字符串
type User = Record
const user: User = {
id: ‘123’,
name: ‘John Doe’,
email: ‘[email protected]’,
};
// 类型安全检查:
// 1. 不允许出现额外的键
// const invalidUser: User = { id: ‘123’, name: ‘Jane Doe’, age: 30 };
// Error: Object literal may only specify known properties, and ‘age’ does not exist in type ‘User’.
// 2. 必须包含所有指定的键
// const incompleteUser: User = { id: ‘456’, name: ‘Jane Doe’ };
// Error: Property ’email’ is missing in type ‘{ id: string; name: string; }’ but required in type ‘User’.
console.log(‘\n— 结合联合类型 —‘);
console.log(user);
“`
何时使用:当你需要确保一个对象严格符合某个形状,拥有所有必需的属性且没有多余属性时,这个方法非常有用。
3. 值为复杂类型:构建配置对象
Record 的值(Type)可以是任何复杂的类型,包括联合类型、接口或其他的对象类型。这让我们可以构建非常复杂的、但依然类型安全的数据结构。
“`typescript
// 定义配置项的值可以是多种类型
type ConfigValue = string | number | boolean;
// 定义一个应用配置类型
type AppConfig = Record
const appConfig: AppConfig = {
‘api-key’: ‘xyz-123-abc’,
‘max-retries’: 3,
‘use-https’: true,
};
// 类型安全检查:不允许出现 ConfigValue 中未定义的类型
// const invalidConfig: AppConfig = { ‘use-cache’: null };
// Error: Type ‘null’ is not assignable to type ‘string | number | boolean’.
console.log(‘\n— 值为复杂类型 —‘);
console.log(appConfig);
“`
4. 实战场景:管理动态 ID 的数据集合
这是 Record 最经典的用例之一。假设你正在从一个 API 拉取用户数据,并希望将这些数据存储在一个对象中,以便通过用户 ID 快速查找。用户 ID 是动态的,无法提前预知。
“`typescript
// 首先,定义单个用户的资料结构
interface UserProfile {
name: string;
role: ‘admin’ | ‘user’ | ‘guest’;
createdAt: Date;
}
// 定义用户数据存储类型:键是用户 ID (string),值是用户的个人资料 (UserProfile)
type UserDataStore = Record
const userData: UserDataStore = {
‘user-1a2b’: { name: ‘Alice’, role: ‘admin’, createdAt: new Date() },
‘user-3c4d’: { name: ‘Bob’, role: ‘user’, createdAt: new Date() },
};
// 添加新用户时,TypeScript 会强制我们遵循 UserProfile 结构
userData[‘user-5e6f’] = { name: ‘Charlie’, role: ‘user’, createdAt: new Date() };
// 错误示例:
// 1. 结构不完整
// userData[‘user-7g8h’] = { name: ‘David’ };
// Error: Property ‘role’ is missing…
// 2. 值不符合类型定义
// userData[‘user-8i9j’] = { name: ‘David’, role: ‘super-admin’, createdAt: new Date() };
// Error: Type ‘”super-admin”‘ is not assignable to type ‘”admin” | “user” | “guest”‘.
// 创建一个安全的查找函数
function getUserProfile(userId: string): UserProfile | undefined {
return userData[userId];
}
console.log(‘\n— 实战场景:用户数据存储 —‘);
const foundUser = getUserProfile(‘user-3c4d’);
console.log(‘找到用户:’, foundUser?.name); // 输出: 找到用户: Bob
const notFoundUser = getUserProfile(‘user-unknown’);
console.log(‘未找到用户:’, notFoundUser); // 输出: 未找到用户: undefined
“`
何时使用:这对于管理从数据库或 API 返回的数据集合非常理想,可以轻松地创建一个规范化的数据存储(像一个小型的内存数据库)。
总结
TypeScript 的 Record 工具类型是增强代码健壮性的利器。它让你能够在处理动态键的场景下,依然享受到静态类型检查带来的所有好处——更早地发现错误、更好的代码提示和更高的可维护性。下次当你需要创建一个字典或查找表时,请务必考虑使用 Record。