简体   繁体   English

在一个 React 组件中在两种不同的 prop 类型之间切换

[英]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允许您设置可以使用的有效类型( RGRGIC )并默认为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.

相关问题 React Typescript 区分不同的道具类型,从两种类型中选择一种 - React Typescript distinguish between different prop types, choosing from one of two types React TypeScript 组件具有两个不同的 prop 接口 - React TypeScript component with two different prop interfaces React PropTypes:允许一个道具使用不同类型的 PropTypes - React PropTypes: Allow different types of PropTypes for one prop React 在组件中为相同的 prop 显示两个不同的值 - React shows two different value for same prop in component 在同一个React组件的不同实例之间切换时使用componentDidMount - Utilizing componentDidMount when switching between different instances of the same React component 我有一个<add />在两个不同的页面中调用的组件,但其中只有一个正在接收在 React JS 中传递给它的添加计数道具 - I have an <Add /> component that is called in two different pages, but only one of them is receiving the add count prop passed to it in React JS 流类型:可扩展的React Component道具类型 - Flow type: extendable React Component prop types 反应 Typescript 组件与多种道具类型 - React Typescript Component with Multiple Prop Types 使用泛型提取反应组件道具类型 - Extract react component prop types with generics 用于React组件的Typescript prop类型的语法? - Syntax for Typescript prop types for React component?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM