【发布时间】:2021-11-02 07:39:06
【问题描述】:
TL;DR:Here's the TS playground.
我在使用 React 组件道具类型定义时遇到了一些问题。我在 npm 上发布了一个组件,该组件目前采用一组简单的 props,主要是布尔值和字符串,其中一个 props 类型为 RG 和一些基于 RG 类型的回调函数。
我想要提供的是相同的组件,但带有一个可选的boolean 属性,当true 时,“翻转”组件以使用新的RGIC 类型而不是RG。假设这是当前的Props 接口:
interface Props {
x: string;
y: string;
rg?: RG;
func(rg: RG): void;
}
我想用这个接口提供一个组件的重载:
interface PropsIC {
x: string;
y: string;
rg?: RGIC;
func(rg: RGIC): void;
}
保持向后兼容性很重要,因此新道具在外部 API 中必须是可选的。
这些是我想出的类型:
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;
这就是我使用它们的方式:
注意:我确信上面的类型可以使用一些改进,所以请随意评论这些,但下面的代码块是我的问题所在。
// 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>);
}
这是用户实现组件的方式:
// 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);
我了解如何使用自定义类型保护和 in 运算符(如 here 和 here),但我不确定如何将它们应用于整个组件函数体而不复制整个实施。
更新:this TS playground 已根据 @chazsolo 的评论更新了类型。
【问题讨论】:
-
请提供最小可重现示例
-
TS 游乐场的例子还不够吗?
-
没关系,但如果你把它最小化一点会好很多
-
您是否考虑过键入组件而不是重载?像
export function Component<T extends RG | RGIC = RG>(props: Props<T>) {这样的东西然后使用通用的T类型作为useState? -
是的,@chazsolo,我认为做到了!我已经通过指向更新的 TS 游乐场的链接更新了问题。想要提供您的评论作为答案,以便我将其标记为解决方案?
标签: reactjs typescript