mcpField
R
record-types-sync

record-types-sync

github.com/marius-townhouse开发643 次安装38 周前更新
代码仓库

关于 record-types-sync

record-types-sync 是一项 TypeScript 技能,使用 Record 类型让类型定义与相关配置或数据结构保持一致。新增属性时,编译器会指出缺少的条目,有助于维护验证器、默认值、标签等逐属性设置。

主要功能

  1. 01用 Record 确保属性完整覆盖
  2. 02保持并行数据结构同步
  3. 03在编译时发现缺失条目
  4. 04配置验证器、默认值和标签

适用场景

  1. 01要求为每个类型属性提供设置
  2. 02让更新检查与属性保持一致
  3. 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>
));

抗压流程

维护并行结构时:

  1. 识别耦合关系:哪些结构必须保持同步?
  2. 使用 Record<keyof T, V>:确保完整覆盖
  3. 选择有意义的值类型:boolean、函数、string 等
  4. 记录此模式:说明使用 Record 的原因
  5. 处理可选属性:必要时使用 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