自动将界面分为基础和附加功能

时间:2018-06-22 18:14:36

标签: javascript typescript

假设我的这些接口定义如下。

interface ButtonProps {
    text: string;
}

interface DescriptiveButtonProps extends ButtonProps {
    visible: boolean,
    description: string;
}

而且,我正在尝试使用接口中定义的额外属性来渲染一个DescriptiveButton组件的Button

class DescriptiveButton extends React.Component<DescriptiveButtonProps, {}> {
    render () {
        const { visible, description, ...rest } = this.props;
        return visible ? <div>{description}: <Button {...rest}/></div> : <div />;
    }
}

如您所见,我不得不手动列出所有其他道具。 visibledescription。我想将DescriptiveButtonProps分成ButtonProps和其他内容,而不必全部列出。有没有办法做到这一点?

1 个答案:

答案 0 :(得分:2)

如果要将一个对象分成两个对象,则需要指定要在第一个输出对象中看到的键。这样的事情会起作用:

function split<T, K extends keyof T>(
  obj: T, keys: K[]
): [Pick<T, K>, Pick<T, Exclude<keyof T, K>>] {
  const pick = {} as Pick<T, K>;
  const unpick = {} as Pick<T, Exclude<keyof T, K>>;
  const keySet = {} as Record<K, boolean>;
  keys.forEach(k => keySet[k] = true);
  (Object.keys(obj) as (keyof T)[]).forEach(k => {
    if (k in keySet) {
      const kk = k as K;
      pick[kk] = obj[kk];
    } else {
      const kk = k as Exclude<keyof T, K>
      unpick[kk] = obj[kk];
    }
  });
  return [pick, unpick];
}

您可以在render()方法中使用它:

const [xp , rest] = split(this.props, ["visible", "description"]);
// xp.visible, xp.description, and rest.text

这并不比您已经做的好,因为您要在数组中再次键入"visible""description"。但是数组比destructuring assignment中的变量名称更易于操作,因此我们正朝着减少这种冗余的方向发展。


从这里开始,我们的想法是提出一个运行时对象,其键为"visible""description",而无需键入两次。问题是您不能从接口派生值。运行时类型系统为erased。您可以执行相反的操作:从值派生接口。这是针对您的情况的一种方法:

interface ButtonProps {
  text: string;
}

const descriptiveButtonExtraProps = {
  visible: true,
  description: "string"
}
type DescriptiveButtonExtraProps = typeof descriptiveButtonExtraProps;

type PropertyIntersect<T, U> = { [K in keyof (T & U)]: (T & U)[K] }; 
interface DescriptiveButtonProps extends 
  PropertyIntersect<ButtonProps, DescriptiveButtonExtraProps> { };

这可能很难理解,但是最后,DescriptiveButtonProps正是您以前拥有的,但是您是从值descriptiveButtonExtraProps派生出来的。这就是我们将用于获取拆分键列表的值:

const descriptiveButtonExtraKeys = Object.keys(descriptiveButtonExtraProps) as 
  (keyof DescriptiveButtonExtraProps)[];

最后,我们可以重写您的render()方法:

class DescriptiveButton extends React.Component<DescriptiveButtonProps, {}> {
  render () {
    const [xp, rest]: [DescriptiveButtonExtraProps, ButtonProps] = 
      split(dp, descriptiveButtonExtraKeys);
    return xp.visible ? <div>{xp.description}: <Button {...rest}/></div> : <div />;
  }
}

就这样了。


所有这些都应该发挥作用,但这是一堆额外的机制,您需要四处寻找来保存重复的键名。仅当您有很多键名或键名列表经常变化时,这才值得。您不必确保解构分配中的变量名称与DescriptiveButtonProps的定义相匹配。但是,如果您有一些属性,或者它们不经常更改,那么最好以使代码保持所拥有的方式并保持谨慎的态度可能会更好。由你决定。

希望有帮助。祝你好运!