reactjs - 使用联合类型泛型时推断单个类型(扩展反应组件道具)
问题描述
我正在尝试定义一个接受许多预定义属性的包装 React 组件。属性之一 ( tag
) 的存在和值还应确定是否有其他属性可用。react 组件将充当所提供tag
组件的包装器,该组件将使用包装组件未专门处理的任何道具进行渲染。如果tag
省略,则包装器仍然可以接受未处理的道具并将其转发给子组件,但不提供任何类型(简单Record<string, any>
或类似的就可以了)。
不幸的是,我还没有找到一种方法来阻止 Typescript 在tag
省略道具时生成所有可能属性的联合。
Codesandbox上提供了该问题的最小复制。我试图解决的问题在其中得到了进一步的解释,但是TL;DR:
<Field tag="textarea" name="test" cols={10} /> // cols is correctly typed
<Field tag="input" name="test" cols={10} /> // cols is correctly marked as invalid
<Field name="omitted" cols={10} /> // cols is typed here even though it's not a prop on <input>
代码摘录:
import * as React from "react";
import * as ReactDOM from "react-dom";
export type FieldTagType = React.ComponentType | "input" | "select" | "textarea";
type TagProps<Tag> = Tag extends undefined ? {}
: Tag extends React.ComponentType<infer P> ? P
: Tag extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[Tag]
: never;
export type FieldProps<Tag extends FieldTagType> = {
/** Field name */
name: string;
/** Element/component to createElement(), defaults to 'input' */
tag?: Tag;
};
function Field<Tag extends FieldTagType>({
name,
tag,
...props
}: FieldProps<Tag> & TagProps<Tag>) {
return React.createElement(tag || "input", {
name,
id: name + "-id",
placeholder: name,
...props
} as any);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
{/*
<textarea> supports the `cols` attribute, as expected.
*/}
<Field tag="textarea" name="textarea" cols={10} />
{/*
<input> doesn't support the `cols` attribute, and
since `tag` is properly set typescript will warn about it.
*/}
<Field tag="input" name="test" cols={10} />
{/*
When `tag` is omitted it appears that FieldTagType
is expanded into every possible type, rather than falling
back to not providing any additional properties.
This results in `cols` being included in the list of defined props,
even though it is not supported by the `input` being rendered as fallback.
Is it possible to modify the typings of TagProps/FieldProps/Field
so that omitting `tag` will not include any additional properties,
but also not error?
Eg. TagProps are expanded to `Record<string, any>`
*/}
<Field name="omitted" cols={10} />
</React.StrictMode>,
rootElement
);
解决方案
当您在其参数中Field
没有tag
属性的情况下调用时,这不会导致编译器推断"input"
或什undefined
至推断Tag
泛型参数。由于tag
是可选属性,因此未定义的值tag
可以应用于 的任何有效选择Tag
。例如,您可以指定Tag
as"textarea"
并仍然保留该tag
属性:
Field<"textarea">({ name: "xyz", cols: 10 }); // no error
当然,这不太可能真正发生。无论如何,当您省略编译器时tag
,编译器不知道Tag
应该是什么,只是将其默认为它的约束, FieldTagType
.
如果您希望它默认为"input"
,您可以设置一个通用参数 default:
function Field<Tag extends FieldTagType = input>({
// default here ----------------------> ~~~~~~~
name,
tag,
...props
}: FieldProps<Tag> & TagProps<Tag>) {
return React.createElement(tag || "input", {
name,
id: name + "-id",
placeholder: name,
...props
} as any);
}
这不会影响您对其他情况的推断,但是当tag
省略时,现在您会得到空TagProps<undefined>
的 forprops
和所需的错误:
<Field name="omitted" cols={10} /> // error!
// -----------------→ ~~~~
// Property 'cols' does not exist on type
// 'FieldProps<"input"> & TagProps<"input">'
推荐阅读
- javascript - setupDemo 中一系列概念的 Hyperledger Fabric 语法?
- python - Pygame Cass Function for creating platforms without manually creating every single platform
- mysql - MySQL Server 8.0.12 - 必须为新用户指定身份验证插件
- python - 为什么 python 命令在 py 工作时不会在 powershell 中调用 python 解释器
- node.js - 詹金斯不运行量角器测试
- node.js - 承诺多个 .then 当一个 then 指望另一个
- android - 使用 libjingle 在 android WebRTC 中自定义音频设备
- java - 带通配符的静态导入
- mysql - 无法在 Excel 2016 VBA 中连接到 mysql 数据库
- javascript - 通过单击添加新文本时需要调整文本区域的大小