使用道具传递要渲染的另一个组件(+ props)并保持类型安全

时间:2018-08-09 08:45:14

标签: reactjs typescript

我想将Component传递给包装器组件,包括要在包装器组件内部呈现的道具。 Typescript应该验证传递的道具是否属于传递的组件并进行类型检查。

这是传递给组件的道具:

act?:componentAct<T>

这部分起作用:

interface componentAct<T>  {
  component: React.ComponentType<T>,
  props:T
} 

interface WrapperProps<T> {
  act?: componentAct<T>
}

interface ActorProps {
  name: string
}

const Wrapper: React.SFC<WrapperProps<ActorProps>> = props => {
  const componentAct = props.act
  const Comp = componentAct!.component
  return <Comp {...componentAct!.props}>{props.children}</Comp>
}


const Actor = (props: ActorProps) => {
  return <div {...props}>test123</div>
}

const appNode = document.getElementById('app')

ReactDOM.render((<Wrapper act={{component: Actor, props:{name:'test'}}} />), appNode);

但是

  

React.SFC >

我不想在这里传递ActorProps(而不是问号),因为包装器的props不知道Actor使用的是props,因为Wrapper在Actor的另一个库中。

这里的另一个问题是组件可能没有道具(然后应从componentAct中省略道具)

1 个答案:

答案 0 :(得分:1)

如果您使用的是2.9或更高版本,则可以使用通用组件,该组件将允许您传递通用参数。我们还可以使用条件类型来使props字段仅在组件具有道具时才需要:

interface componentAct<T> {
    component: React.ComponentType<T>,
    props: T
}

interface WrapperProps<T> {
    act?: keyof T extends never ? { component: React.ComponentType<T>, props?: never } : componentAct<T>
}

interface ActorProps {
    name: string
}

const Wrapper = function <T>(props: WrapperProps<T> & { children?: React.ReactNode }) {
    const componentAct = props.act
    const Comp = componentAct!.component
    return <Comp {...componentAct!.props}>{props.children}</Comp>
}

const Actor = (props: ActorProps) => {
    return <div {...props}>test123</div>
}

let ok = (<Wrapper<ActorProps> act={{ component: Actor, props: { name: 'test' } }} />)
let errorNoProp = (<Wrapper<ActorProps> act={{ component: Actor }} />) // Property 'props' is missing in type '{ component: (props: ActorProps) => Element; }'.
let errorExtraProp = (<Wrapper<ActorProps> act={{ component: Actor, props: { name: 'test', s: "" } }} />) // Object literal may only specify known properties, and 's' does not exist in type 'ActorProps'.
let errorForgottenGenericParam = (<Wrapper act={{ component: Actor, props: { name: 'test' } }} />)

const NoParams = () => {
    return <div>test123</div>
}

let okNoPropsRequired = (<Wrapper act={{ component: NoParams }} />)
let errorPropsExtra = (<Wrapper act={{ component: NoParams, props: { bla: "" } }} />)