繁体   English   中英

如何使用条件道具接口定义 React 组件?

[英]How to define React component with conditional props interface?

我需要定义呈现textareainput的“字段”组件取决于道具multiline

我试图这样做:

import React from 'react';

type Props<T extends boolean = boolean> = { multiline: T } & T extends true 
    ? React.HTMLProps<HTMLTextAreaElement> 
    : React.HTMLProps<HTMLInputElement>

export const Field: React.FC<Props> = ({ multiline, ...props }) => { // error here
    const Element = multiline ? 'textarea' : 'input';

    return <Element {...props} onInput={e => {}} />; // error here
}

// usage

const result = (
    <Field onChange={e => console.log(e.target.value)} /> // error here
);

但是 typescript 提供了几个错误,例如:

1 Property 'multiline' does not exist on type 'HTMLProps<HTMLInputElement> & { children?: ReactNode; }'.(2339)

2 [large error, more in playground]

3 Property 'value' does not exist on type 'EventTarget'.(2339)

这里的游乐场

我如何定义这样的组件?

我相信你应该使用这两种类型的交集,所以它可以是/或。 因此,对于Props类型,使用“&”运算符来覆盖两种输入类型,然后在onChange事件中再次为每个输入类型事件使用它(因为它可以是任何一种)。 像这样的东西:

import React from 'react';

type Props<T extends boolean = boolean> = { multiline?: T } &
    React.HTMLProps<HTMLTextAreaElement> &
    React.HTMLProps<HTMLInputElement>;

export const Field: React.FC<Props> = ({ multiline, ...props }) => {
    const Element = multiline ? 'textarea' : 'input';

    return <Element {...props} />;
}

// usage

const result = (
    <Field onChange={(e: React.ChangeEvent<HTMLInputElement> & React.ChangeEvent<HTMLTextAreaElement>) => (
        console.log(e.target.value)
    )} />
);

问题:字段中没有T

您已经定义了一个依赖于T的泛型类型Props ,但您的组件不是泛型的。 它总是需要Props<boolean>解析为HTMLInputElement道具,因为boolean extends truefalse {multiline: boolean}丢失的原因是因为您需要在您的类型的 rest 周围加上括号。

React.HTMLProps

使用React.HTMLProps类型时,将不匹配的属性(如type="number"分配给textarearows={5}分配给input )时没有出现错误。 更严格的类型是JSX.IntrinsicElements['textarea']JSX.IntrinsicElements['input'] (它们解析为React.DetailedHTMLProps<React.TextareaHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement>类的类型)。 如果您想严格执行,请使用这些! 这也使得onChange回调中的e值根据元素获得正确的类型。

执行

当使用具有限制类型的通用组件时,我们现在在return <Element {...props} />; 我认为将它分成两部分( return multiline? <textarea {...props} />: <input {...props}/>; )会有所帮助,但我们仍然会遇到错误。 条件很粗糙。 您可以as断言来解决问题。 当 function 的使用保持严格类型时,我通常可以在实现 function 时做出断言。 所以你可以这样做:

type Props<T extends boolean = boolean> = { multiline: T } & (T extends true
    ? JSX.IntrinsicElements['textarea']
    : JSX.IntrinsicElements['input'])

export const Field = <T extends boolean>({ multiline, ...props }: Props<T>) => {
    const Element = multiline ? 'textarea' : 'input';

    return <Element {...props as any} />;
}

游乐场#1

联合类型

我们可以通过键入Props作为两种情况的并集来避免做出断言。 这使我们可以通过查看props.multiline来检查联合中的类型。 虽然这会变得很混乱,因为你区分联合之前你不能解构,但我们不想将多行传递给 DOM。

此代码通过了所有类型检查,但需要额外的工作来防止将multiline传递到 DOM。

type Props = ( 
    { multiline: true } & JSX.IntrinsicElements['textarea'] | 
    { multiline: false } & JSX.IntrinsicElements['input']
);

export const Field = ({ ...props }: Props) => {
    return props.multiline ? <textarea {...props} /> : <input {...props}/>
}

游乐场 #2

用法

无论哪种方式,用法都是非常强类型的! 我们的onChange回调获得了正确的类型,如React.ChangeEvent<HTMLTextAreaElement> ,如果在multiline={false}时传递textarea道具,我们会得到错误,反之亦然。

<Field
    onChange={e => console.log(e.target.value)} // e: React.ChangeEvent<HTMLTextAreaElement>
    multiline={true}
    rows={5} // ok
    type="number" // error
/>
<Field
    onChange={e => console.log(e.target.value)} // e: React.ChangeEvent<HTMLInputElement>
    multiline={false}
    type="number" // ok
    rows={5} // error
/>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM