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

IT技术 reactjs typescript
2021-05-21 08:20:47

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

这是传递给组件的props:

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 < WrapperProps< ?>>

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

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

1个回答

如果您使用 2.9 或更高版本,您可以使用泛型组件,它允许您传入泛型参数。我们还可以使用条件类型来使该props字段仅在组件具有 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: "" } }} />)