一起养成写作习惯!这是我参与「掘金日新计划 · 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拥有完全同样的属性.