[英]Switching between two different prop types in one React component
TL;DR: Here's the TS playground . TL;DR: 这里是 TS 游乐场。
I'm having some trouble with a React component props type definition.我在 React 组件 props 类型定义方面遇到了一些问题。 I have a component published on npm that currently takes a simple set of props, mostly booleans and strings, with one prop of type RG
and some callback functions based around the RG
type.我在 npm 上发布了一个组件,它目前采用一组简单的 props,主要是布尔值和字符串,一个是RG
类型的 props 和一些基于RG
类型的回调函数。
What I want to provide is the same component, but with an optional boolean
prop that, when true
, "flips" the component to use a new RGIC
type instead of RG
.我想提供的是相同的组件,但有一个可选的boolean
RGIC
,当true
,“翻转”组件以使用新的RGIC
类型而不是RG
。 Assume this is the current Props
interface:假设这是当前的Props
界面:
interface Props {
x: string;
y: string;
rg?: RG;
func(rg: RG): void;
}
I would like to offer an overload of the component with this interface:我想通过此接口提供组件的重载:
interface PropsIC {
x: string;
y: string;
rg?: RGIC;
func(rg: RGIC): void;
}
It's important that I retain backwards compatibility, so the new prop must be optional in the external API.保留向后兼容性很重要,因此新的 prop 在外部 API 中必须是可选的。
These are the types I've come up with:这些是我想出的类型:
type RGAny = RG | RGIC;
interface CommonProps {
x: string;
y: string;
flag: boolean; // <-- this is the new prop
}
interface PropsStandard extends CommonProps {
flag: false;
query?: RG;
func(q: RG): void;
}
interface PropsIC extends CommonProps {
flag: true;
query?: RGIC;
func(q: RGIC): void;
}
type Props = (Omit<PropsStandard, 'flag'> & { flag?: boolean }) | (Omit<PropsIC, 'flag'> & { flag?: boolean });
type PropsImpl = PropsStandard | PropsIC;
And this is how I am using them:这就是我使用它们的方式:
NOTE: I'm sure the types above could use some improvements, so feel free to comment on those, but the code block below is where the meat of my question lies.注意:我确信上面的类型可以使用一些改进,所以请随意评论这些,但下面的代码块是我的问题所在。
// This is the exported component, the API surface
export function Component(props: Props) {
if (props.flag) {
return ComponentImpl({ ...props, flag: true } as PropsIC)
}
return ComponentImpl({ ...props, flag: false } as PropsStandard);
}
// This is the component's internal implementation with two overloads,
// one for RG props (flag is undefined or false) and one for RGIC props (flag === true).
function ComponentImpl(props: PropsStandard): JSX.Element;
function ComponentImpl(props: PropsIC): JSX.Element;
function ComponentImpl({ query, func, flag, x, y }: PropsImpl) {
// On this next line, I want to declare RG or RGIC instead of RGAny,
// based on the value of the flag prop (or whether props is PropsStandard or PropsIC).
const [root, setRoot] = useState<RGAny>(query ?? { combinator: 'and', rules: [] });
useEffect(() => {
func(root) // <-- TS error here because RGAny is not assignable to RG
}, [root])
return (<div>{flag ? 'True' : 'False'}</div>);
}
This is how the user might implement the component:这是用户实现组件的方式:
// The user's application component
const App = () => {
const [query, setQuery] = useState<RG>({ combinator: 'and', rules: [] })
const [queryIC, setQueryIC] = useState<RGIC>({ rules: [] })
const [flag, setFlag] = useState(false);
return flag
? <Component x="test" y="test" query={queryIC} func={(q: RGIC) => console.log(q)} flag />
: <Component x="test" y="test" query={query} func={(q: RG) => console.log(q)} />;
}
ReactDOM.render(<App />, document.body);
I understand how to use custom type guards and the in
operator (like here and here ), but I'm not sure how to apply them to the entire component function body without sort of duplicating the entire implementation.我了解如何使用自定义类型保护和in
运算符(如此处和此处),但我不确定如何将它们应用到整个组件函数体而不复制整个实现。
Update: this TS playground has updated typings based on @chazsolo's comment.更新: 此 TS 游乐场已根据 @chazsolo 的评论更新了类型。
Instead of adding an optional flag to your component, consider adding a generic type similar to (not complete code):与其向组件添加可选标志,不如考虑添加类似于(非完整代码)的泛型类型:
interface Props<T> & CommonProps {
func(q: T): void;
query: T;
}
function Component<T extends RG | RGIC = RG>(props: Props<T>) {
// ...
}
T extends RG | RGIC = RG
T extends RG | RGIC = RG
allows you to set the valid types that can be used ( RG
or RGIC
) and defaults to RG
in case a user doesn't pass one in. The correct type will then be inferred based on what was given to the component ( so things like the parameters in func
don't need to be re-typed when used ). T extends RG | RGIC = RG
允许您设置可以使用的有效类型( RG
或RGIC
)并默认为RG
以防用户未传入。然后将根据提供给组件的内容推断正确的类型(所以像func
的参数这样的东西在使用时不需要重新输入)。
Usage will look like:用法如下:
return flag
? <Component<RGIC> query={queryIC} func={(q) => console.log(q)} />
: <Component<RG> query={query} func={(q) => console.log(q)} />;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.