Reactjs 使用联合类型泛型时推断单个类型(扩展react组件道具)
我试图定义一个包装器React组件,它接受许多预定义的属性。其中一个属性(Reactjs 使用联合类型泛型时推断单个类型(扩展react组件道具),reactjs,typescript,typescript-generics,Reactjs,Typescript,Typescript Generics,我试图定义一个包装器React组件,它接受许多预定义的属性。其中一个属性(标记)的存在和值还应确定是否有其他属性可用。react组件将充当所提供的标记组件的包装器,该组件将使用包装组件未专门处理的任何道具进行渲染。如果省略了标记,包装器仍然可以接受未处理的道具并将其转发给子组件,但不提供任何类型(一个简单的记录或类似的记录就可以了) 不幸的是,我还没有找到一种方法来防止Typescript在省略标记时生成所有可能属性的并集 可在上获得该问题的最低复制版本。我试图解决的问题在书中有进一步的解释,但
标记
)的存在和值还应确定是否有其他属性可用。react组件将充当所提供的标记
组件的包装器,该组件将使用包装组件未专门处理的任何道具进行渲染。如果省略了标记
,包装器仍然可以接受未处理的道具并将其转发给子组件,但不提供任何类型(一个简单的记录
或类似的记录就可以了)
不幸的是,我还没有找到一种方法来防止Typescript在省略标记时生成所有可能属性的并集
可在上获得该问题的最低复制版本。我试图解决的问题在书中有进一步的解释,但是TL;医生:
<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
);
//cols键入正确
//cols被正确标记为无效
//cols在这里输入,即使它不是道具
代码摘录:
<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
);
import*as React from“React”;
从“react dom”导入*作为react dom;
导出类型FieldTagType=React.ComponentType |“输入”|“选择”|“文本区域”;
类型TagProps=标记扩展未定义?{}
:Tag.ComponentType?P
:标记JSX.intrinsiceelements的名称?JSX.intrinsiceelements[Tag]
:从不;
导出类型FieldProps={
/**字段名*/
名称:字符串;
/**元素/组件到createElement(),默认为“输入”*/
标签?:标签;
};
函数域({
名称
标签,
…道具
}:现场道具和标语道具){
返回React.createElement(标记| |“输入”{
名称
id:name+“-id”,
占位符:名称,
…道具
}(如有的话);
}
const rootElement=document.getElementById(“根”);
ReactDOM.render(
{/*
如预期的那样,支持'cols'属性。
*/}
{/*
不支持'cols'属性,并且
由于'tag'的设置正确,因此typescript将对此发出警告。
*/}
{/*
省略“tag”时,FieldTagType
扩展到所有可能的类型,而不是下降
返回到不提供任何附加属性。
这导致“cols”被包括在已定义的道具列表中,
即使“input”作为回退提供不支持它。
是否可以修改TagProps/FieldProps/Field的类型
因此省略“tag”将不包括任何附加属性,
但也不是错误吗?
TagProps扩展为“记录”`
*/}
,
根元素
);
当调用字段时,其参数中没有标记
属性,这不会导致编译器为标记
泛型参数推断输入
,甚至未定义
。由于tag
是可选属性,因此tag
的未定义值可应用于任何有效的tag
选择。例如,您可以将Tag
指定为“textarea”
,但仍保留Tag
属性:
Field<"textarea">({ name: "xyz", cols: 10 }); // no error
这不会影响其他情况下的推断,但如果省略了标记
,则现在您会得到道具
的空道具
,以及所需的错误:
<Field name="omitted" cols={10} /> // error!
// -----------------→ ~~~~
// Property 'cols' does not exist on type
// 'FieldProps<"input"> & TagProps<"input">'
//错误!
// -----------------→ ~~~~
//类型上不存在属性“cols”
//‘现场道具<输入>和标签道具<输入>’
也许可以尝试函数字段
将默认标记类型设置为“input”@JacobSmit谢谢您的建议!谢谢你的评论,非常感谢!我完全忘了提到我曾尝试将'input'
指定为通用参数默认值,正如您所写的那样,它确实解决了整个问题。唯一的缺点是,由于添加了大量输入属性,这使得在intellisense/LSP属性/属性完成列表(至少在我的vim设置中)中更难发现特定于组件的属性。我觉得这方面没有办法改进,所以除非有人有办法解决这个问题,否则我会将这个答案标记为正确答案。例如,如果需要提供一个标签来包含我可以从开始的附加属性,那么可能有更好的东西,但我不知道我是否能承诺花更多的时间来研究它。祝你好运