[译]<<Effective TypeScript>> 高效TypeScript62个技巧 技巧18

333 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情

本文的翻译于<<Effective TypeScript>>, 特别感谢!! ps: 本文会用简洁, 易懂的语言描述原书的所有要点. 如果能看懂这文章,将节省许多阅读时间. 如果看不懂,务必给我留言, 我回去修改.

技巧18: 使用映射类型保持值同步

如果你正在为一个散点图写一个ui 组件, 他有一些不同的属性控制组件的显示和行为:

interface ScatterProps {
  // The data
  xs: number[];
  ys: number[];

  // Display
  xRange: [number, number];
  yRange: [number, number];
  color: string;

  // Events
  onClick: (x: number, y: number, index: number) => void;
}

你肯定想让它按需更新视图, 改变数据或者视图参数要求重新绘制图形, 但是改变 event handler 不重新绘制. 这种优化在react中很常见. 这可能是你的一种实现方式:

function shouldUpdate(
  oldProps: ScatterProps,
  newProps: ScatterProps
) {
  let k: keyof 
ScatterProps;
  for (k in oldProps) {
    if (oldProps[k] !== newProps[k]) {
      if (k !== 'onClick') return true;
    }
  }
  return false;
}

如果ScatterProps增加一个新的属性会发生什么? 那么shouldUpdate始终都会重新绘制chart.

这种函数可能会被认为 '过于保守' 或者 'fail closed'. 因为散点图始终是正确的, 只是更新过于频繁了.

反之则称为 'fail open' , 长这样:

function shouldUpdate(
  oldProps: ScatterProps,
  newProps: ScatterProps
) {
  return (
    oldProps.xs !== newProps.xs ||
    oldProps.ys !== newProps.ys ||
    oldProps.xRange!== newProps.xRange ||
    oldProps.yRange !== newProps.yRange ||
    oldProps.color !== newProps.color
    // (no check for onClick)
  );
}

这种方法避免了任何不必要的重绘, 但是有可能某些必要的重绘被抛弃.

两种方法都不是很理想. 你可能会添加注释来提醒其他人:

interface ScatterProps {
  xs: number[];
  ys: number[];
  // ...
  onClick: (x: number, y: number, index: number) => void;

  // Note: if you add a property here, update shouldUpdate!
}

仅仅期待注释有效是不够的, 最好利用类型检查强制规定:

const REQUIRES_UPDATE: {[k in keyof ScatterProps]: boolean} = {
  xs: true,
  ys: true,
  xRange: true,
  yRange: true,
  color: true,
  onClick: false,
};

function shouldUpdate(
  oldProps: ScatterProps,
  newProps: ScatterProps
) {
  let k: keyof ScatterProps;
  for (k in oldProps) {
    if (oldProps[k] !== newProps[k] && REQUIRES_UPDATE[k]) {
      return true;
    }
  }
  return false;
}

[k in keyof ScatterProps]告诉了 类型检查器, REQUIRES_UPDATES 必须拥有所有属性. 如果有个人在ScatterProps增加了属性:

interface ScatterProps {
  // ...
  onDoubleClick: () => void;
}

然后类型检查会报错:

const REQUIRES_UPDATE: {[k in keyof ScatterProps]: boolean} = {
  //  ~~~~~~~~~~~~~~~ Property 'onDoubleClick' is missing in type
  // ...
};

同样的可以约束array:

const PROPS_REQUIRING_UPDATE: (keyof ScatterProps)[] = [
  'xs',
  'ys',
  // ...
];

映射类型 保证了两个object拥有完全同样的属性.