Javascript 自动将接口拆分为基本接口和附加接口

Javascript 自动将接口拆分为基本接口和附加接口,javascript,typescript,Javascript,Typescript,假设这些接口定义如下 interface ButtonProps { text: string; } interface DescriptiveButtonProps extends ButtonProps { visible: boolean, description: string; } 而且,我正在尝试渲染一个DescriptiveButton,它使用界面中定义的额外属性渲染一个按钮组件 class DescriptiveButton extends React

假设这些接口定义如下

interface ButtonProps {
    text: string;
}

interface DescriptiveButtonProps extends ButtonProps {
    visible: boolean,
    description: string;
}
而且,我正在尝试渲染一个
DescriptiveButton
,它使用界面中定义的额外属性渲染一个
按钮
组件

class DescriptiveButton extends React.Component<DescriptiveButtonProps, {}> {
    render () {
        const { visible, description, ...rest } = this.props;
        return visible ? <div>{description}: <Button {...rest}/></div> : <div />;
    }
}
class DescriptiveButton扩展了React.Component{
渲染(){
const{visible,description,…rest}=this.props;
返回可见?{description}::;
}
}

如你所见,我不得不手动列出所有额外的道具<代码>可见和
说明
。我想把
DescriptiveButtonProps
分为
ButtonProps
和其他部分,而不必全部列出。有没有办法做到这一点?

如果要将一个对象拆分为两个对象,需要指定要在第一个输出对象中看到的关键点。类似这样的方法会奏效:

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];
}
这并不比您已经在做的更好,因为您正在数组中再次键入
“可见”
“说明”
。但是数组比变量中的变量名更容易操作,所以我们正朝着减少冗余的方向前进


从这里开始,我们的想法是创建一个运行时对象,其键是
“可见”
“描述”
,而无需键入两次。问题是,您无法从接口派生值;类型系统处于运行时。您可以做相反的事情:从值派生接口。在您的案例中,有一种方法:

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> { };
最后,我们可以重写您的
render()
方法:

const [xp , rest] = split(this.props, ["visible", "description"]);
// xp.visible, xp.description, and rest.text
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 />;
  }
}
class DescriptiveButton扩展了React.Component{
渲染(){
常量[xp,rest]:[DescriptiveButtonExtraProps,ButtonOps]=
拆分(dp、descriptiveButtonExtraKeys);
返回xp.visible?{xp.description}::;
}
}
好了,结束了


所有这些都应该尽可能地工作,但这是一堆额外的机器,你需要随身携带,以节省重复的键名。只有当你有很多键名或者一个经常变化的键名列表时,这才可能是值得的;您不必一直确保解构赋值中的变量名与
DescriptiveButtonProps
的定义匹配。但是,如果您有一些属性或者它们不经常更改,那么最好保持代码的原有状态,并且要小心。这取决于你

希望有帮助。祝你好运

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 />;
  }
}