关于 record-types-sync
record-types-sync 是一项 TypeScript 技能,使用 Record 类型让类型定义与相关配置或数据结构保持一致。新增属性时,编译器会指出缺少的条目,有助于维护验证器、默认值、标签等逐属性设置。
主要功能
- 01用 Record 确保属性完整覆盖
- 02保持并行数据结构同步
- 03在编译时发现缺失条目
- 04配置验证器、默认值和标签
适用场景
- 01要求为每个类型属性提供设置
- 02让更新检查与属性保持一致
- 03定义完整的验证器或默认值
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill record-types-sync安装后在 Claude Code 中输入/record-types-sync确认已加载。
详细介绍
使用 Record 类型保持值同步
概览
当你有需要保持同步的并行数据结构时——例如某个类型及其属性的配置对象——使用 Record<keyof T, V> 来确保每个属性都得到处理。这样一来,添加类型属性时就会触发编译错误,提醒你更新相关代码。
这种模式对于优化检查、属性验证器,以及任何需要枚举或配置某个类型所有属性的代码都非常有用。
何时使用此技能
- 属性需要使用同步配置
- 添加新属性时需要在其他地方进行更新
- 实现
shouldComponentUpdate风格的优化 - 构建属性验证器或转换器
- 维护并行数据结构
核心规则
使用 Record<keyof T, V> 来确保相关配置对象涵盖 T 的所有属性。
检测
留意以下维护隐患:
// RED FLAGS - Manual synchronization
interface Props {
data: Data;
onClick: () => void;
}
function shouldUpdate(old: Props, new: Props) {
// Manual checks - easy to miss new properties
return old.data !== new.data; // Forgot onClick!
}
// Comments that won't be read:
// Note: if you add a property here, update shouldUpdate!
问题
interface ScatterProps {
xs: number[];
ys: number[];
xRange: [number, number];
yRange: [number, number];
color: string;
onClick?: () => void;
}
// "Fail open" - might redraw too often
function shouldUpdate(old: ScatterProps, new: ScatterProps) {
for (const k in old) {
if (old[k] !== new[k]) {
if (k !== 'onClick') return true; // Forgot new event handlers!
}
}
return false;
}
// "Fail closed" - might miss necessary redraws
function shouldUpdate(old: ScatterProps, new: ScatterProps) {
return (
old.xs !== new.xs ||
old.ys !== new.ys ||
// Forgot xRange, yRange, color!
// Also forgot to exclude onClick
);
}
解决方案:Record 类型
const REQUIRES_UPDATE: Record<keyof ScatterProps, boolean> = {
xs: true,
ys: true,
xRange: true,
yRange: true,
color: true,
onClick: false, // false = change doesn't require redraw
};
function shouldUpdate(old: ScatterProps, new: ScatterProps) {
for (const k in old) {
const key = k as keyof ScatterProps;
if (old[key] !== new[key] && REQUIRES_UPDATE[key]) {
return true;
}
}
return false;
}
现在,添加新属性会迫使你作出决定:
interface ScatterProps {
// ... existing properties
onDoubleClick?: () => void; // New property added
}
// COMPILE ERROR: Property 'onDoubleClick' is missing
const REQUIRES_UPDATE: Record<keyof ScatterProps, boolean> = {
// ... existing entries
// Error reminds you to add: onDoubleClick: ???
};
属性验证器
interface UserInput {
name: string;
email: string;
age: number;
}
// Enforce that every field has a validator
const validators: Record<keyof UserInput, (value: unknown) => boolean> = {
name: (v) => typeof v === 'string' && v.length > 0,
email: (v) => typeof v === 'string' && v.includes('@'),
age: (v) => typeof v === 'number' && v >= 0 && v < 150,
};
// Adding a field forces adding a validator
默认值
interface Config {
timeout: number;
retries: number;
debug: boolean;
}
// Enforce defaults for all properties
const defaults: Record<keyof Config, Config[keyof Config]> = {
timeout: 5000,
retries: 3,
debug: false,
};
function loadConfig(partial: Partial<Config>): Config {
return { ...defaults, ...partial };
}
属性标签
interface FormData {
firstName: string;
lastName: string;
email: string;
}
// Enforce labels for all fields
const labels: Record<keyof FormData, string> = {
firstName: 'First Name',
lastName: 'Last Name',
email: 'Email Address',
};
// Use in UI
Object.entries(formData).map(([key, value]) => (
<label>{labels[key as keyof FormData]}</label>
));
抗压流程
维护并行结构时:
- 识别耦合关系:哪些结构必须保持同步?
- 使用 Record<keyof T, V>:确保完整覆盖
- 选择有意义的值类型:boolean、函数、string 等
- 记录此模式:说明使用 Record 的原因
- 处理可选属性:必要时使用
keyof Required<T>
危险信号
| 反模式 | 问题 | 解决方案 |
|---|---|---|
| 注释写着“Y 变化时更新 X” | 无法强制执行 | Record 类型 |
| 手动枚举属性 | 容易漏掉属性 | 使用带 keyof 的 Record |
| 可选的配置项 | 可能忘记必需项 | 将所有项设为必需 |
常见借口
“我会记得更新它”
事实:你不会,同事也不会。编译器会通过 Record 类型强制执行。
“这只是一个小配置”
事实:小配置会逐渐变大。Record 类型无需额外维护即可扩展。
“有些属性不需要配置”
事实:显式将它们设为 null、false 或空值,总比忘记处理要好。
快速参考
| 使用场景 | Record 类型 | 示例值 |
|---|---|---|
| 优化标志 | Record<keyof T, boolean> | true/false |
| 验证器 | Record<keyof T, ValidatorFn> | 验证函数 |
| 默认值 | Record<keyof T, T[keyof T]> | 默认值 |
| 标签 | Record<keyof T, string> | 显示名称 |
| 转换器 | Record<keyof T, TransformFn> | 转换函数 |
总结
使用 Record<keyof T, V>,确保并行数据结构与类型保持同步。添加新属性时,编译器会提醒你更新相关代码。
参考资料
- Dan Vanderkam 著《Effective TypeScript》第 2 版
- 条目 61:使用 Record 类型保持值同步
评论
支持 Markdown