最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

reactjs - Dynamically type keys for props based on array of strings - Stack Overflow

programmeradmin4浏览0评论

In React I have a component that has render props that can be named dynamically

interface ComponentProps {
  renderPropNames: string[];
  renderProps: Record<string, () => JSX.Element>;
}

The main issue is that I want to use the component that is using this interface

  <Component
    renderPropNames:["prop1", "prop2", "prop3"
    renderProps:{{
      prop1: () => <div>Prop 1</div>
      prop2: () => <div>Prop 2</div>
      prop3: () => <div>Prop 3</div>
    }}
  />

the name of the renderProps can be checked by TypeScript based on the renderPropNames is there a way to do this dynamically in one type/inteface?

I can of course make a type like union of strings

type myStaticPropNames = "prop1" | "prop2" "prop3"

and this will be type checked and will work, but I cannot make the dynamically added strings outside this to be checked

In React I have a component that has render props that can be named dynamically

interface ComponentProps {
  renderPropNames: string[];
  renderProps: Record<string, () => JSX.Element>;
}

The main issue is that I want to use the component that is using this interface

  <Component
    renderPropNames:["prop1", "prop2", "prop3"
    renderProps:{{
      prop1: () => <div>Prop 1</div>
      prop2: () => <div>Prop 2</div>
      prop3: () => <div>Prop 3</div>
    }}
  />

the name of the renderProps can be checked by TypeScript based on the renderPropNames is there a way to do this dynamically in one type/inteface?

I can of course make a type like union of strings

type myStaticPropNames = "prop1" | "prop2" "prop3"

and this will be type checked and will work, but I cannot make the dynamically added strings outside this to be checked

Share Improve this question edited Mar 13 at 10:29 jonrsharpe 122k30 gold badges268 silver badges476 bronze badges asked Mar 11 at 5:52 michal skulamichal skula 112 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 0

Ensuring that the keys in your renderProps object match the entries in your renderPropNames array can be effectively achieved by utilizing TypeScript's type system.

Define the renderPropNames Array with as const:

hence, you create a tuple of string literals

const renderPropNames = ['prop1', 'prop2', 'prop3'] as const;

Derive a Union Type from renderPropNames

This union type will be 'prop1' | 'prop2' | 'prop3', corresponding to the elements of the array.

type RenderPropNames = typeof renderPropNames[number];

Define the ComponentProps Interface Using Mapped Types:

With the RenderPropNames union type, you can construct a mapped type for the renderProps object. This ensures that each key in renderProps corresponds exactly to one of the specified prop names and that each key maps to a function returning a JSX.Element.

  renderPropNames: readonly RenderPropNames[];
  renderProps: { [K in RenderPropNames]: () => JSX.Element };
}

Implement the Component

ensuring it accepts props conforming to the ComponentProps interface. The component will iterate over the renderPropNames array and invoke the corresponding render functions from the renderProps object. for eg.

const Component: React.FC<ComponentProps> = ({ renderPropNames, renderProps }) => (
  <div>
    {renderPropNames.map(name => (
      <div key={name}>
        {renderProps[name]()}
      </div>
    ))}
  </div>
);

lastly, Use the Component with Type Safety:

This strict type checking ensures consistency between the declared prop names and the provided render functions. like,

<Component
  renderPropNames={renderPropNames}
  renderProps={{
    prop1: () => <div>Prop 1</div>,
    prop2: () => <div>Prop 2</div>,
    prop3: () => <div>Prop 3</div>,
  }}
/>

Hope this helps!!!

Modify your ComponentProps interface to use a generic type that infers the keys from renderPropNames:

interface ComponentProps<T extends string[]> {
  renderPropNames: T;
  renderProps: { [K in T[number]]: () => JSX.Element };
}

const Component = <T extends string[]>({ renderPropNames, renderProps }: ComponentProps<T>) => {
  return (
    <div>
      {renderPropNames.map((name) => (
        <div key={name}>{renderProps[name]()}</div>
      ))}
    </div>
  );
};

// Usage
<Component
  renderPropNames={["prop1", "prop2", "prop3"]}
  renderProps={{
    prop1: () => <div>Prop 1</div>,
    prop2: () => <div>Prop 2</div>,
    prop3: () => <div>Prop 3</div>,
  }}
/>;

You can try this one, it will help you.

发布评论

评论列表(0)

  1. 暂无评论