不要为props类型使用泛型条件类型,而是使用区分联合:
type ReactTextAreaProps = React.ComponentPropsWithRef<"textarea">;
type ReactInputProps = React.ComponentPropsWithRef<"input">;
export type TextFieldMultiline =
| ({ multiline: true } & ReactTextAreaProps)
| ({ multiline: false } & ReactInputProps);
然后,该组件如下所示:
export const TextField = (props: TextFieldMultiline) => {
if (props.multiline) {
const { multiline: _, ...rest } = props;
return <textarea {...rest} />;
}
const { multiline: _, ...rest } = props;
return <input {...rest} />;
};
使用示例:
// Valid, allows `textarea`-specific `rows` prop, since `multiline` is `true`:
const a = <TextField multiline={true} rows={10} />;
// Valid, allows `input`-specific `pattern` prop, since `multiline` is `false`:
const b = <TextField multiline={false} pattern="\d+" />;
// INVALID, `multiline` is `true` and `textarea` doesn't have `pattern`:
const c = <TextField multiline={true} pattern="\d+" />;
// ^^^^^^^−−− Error: Type '{ multiline: true; pattern: string; }' is not assignable to type 'IntrinsicAttributes & TextFieldMultiline'. Property 'pattern' does not exist on type...
// INVALID, `multiline` is `false` and `input` doesn't have `rows`:
const d = <TextField multiline={false} rows={10} />;
// ^^^^−−−−− Error: Type '{ multiline: false; rows: number; }' is not assignable to type 'IntrinsicAttributes & TextFieldMultiline'. Property 'rows' does not exist on type...
Playground个
从表面上看,您可能认为可以将析构移到参数列表或组件函数的顶层(这基本上是同一件事),但TypeScrip的流分析无法跟踪到,当您这样做(playground)时,multiline
为真会缩小rest
的类型.有了以上几点,它就可以做到.
我不会这么做,但如果您真的想避免这种重复的析构,您可以使用类型断言like this:
export const TextField = ({ multiline, ...rest }: TextFieldMultiline) => {
return multiline
? <textarea {...rest as ReactTextAreaProps} />
: <input {...rest as ReactInputProps} />;
};
(Note that there's no need for the fragment wrapper in the question.)个
我通常更喜欢没有类型断言的解决方案,因为它们不那么脆弱😊
最后,if你有一个理由,你不能把你的props 类型改变成有区别的联合,你必须坚持使用泛型条件类型,你可以使你的组件函数泛型,以避免multiline
类型的循环问题.但在这种情况下,我认为您无法避免实现中的类型断言:
type ReactTextAreaProps = React.ComponentPropsWithRef<"textarea">;
type ReactInputProps = React.ComponentPropsWithRef<"input">;
export type TextFieldMultiline<Multiline extends boolean> = {
multiline: Multiline;
};
export type TextFieldProps<Multiline extends boolean> =
TextFieldMultiline<Multiline> &
(Multiline extends true
? ReactTextAreaProps
: ReactInputProps);
export const TextField = <Multiline extends boolean>({
multiline,
...props
}: TextFieldProps<Multiline>) => {
return multiline
? <textarea {...props as ReactTextAreaProps} />
: <input {...props as ReactInputProps} />;
};
Playground个